/**
 * @document
 * @namespace Aeon
 * @namespace Aeon.Utils
 */

/*
 * Sizzle CSS Selector Engine - v1.0
 * Copyright 2009, The Dojo Foundation
 * Released under the MIT, BSD, and GPL Licenses.
 * More information: http://sizzlejs.com/
 */
(function(){var chunker=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|[\'\"][^\'\"]*[\'\"]|[^[\]\'\"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,done=0,toString=Object.prototype.toString,hasDuplicate=false;var Sizzle=function(selector,context,results,seed) {results=results||[];var origContext=context=context||document;if(context.nodeType!==1&&context.nodeType!==9) {return[];} if(!selector||typeof selector!=="string") {return results;} var parts=[],m,set,checkSet,check,mode,extra,prune=true,contextXML=isXML(context);chunker.lastIndex=0;while((m=chunker.exec(selector))!==null) {parts.push(m[1]);if(m[2]) {extra=RegExp.rightContext;break;}} if(parts.length>1&&origPOS.exec(selector)) {if(parts.length===2&&Expr.relative[parts[0]]) {set=posProcess(parts[0]+parts[1],context);} else {set=Expr.relative[parts[0]]?[context]:Sizzle(parts.shift(),context);while(parts.length) {selector=parts.shift();if(Expr.relative[selector]) selector+=parts.shift();set=posProcess(selector,set);}}} else {if(!seed&&parts.length>1&&context.nodeType===9&&!contextXML&&Expr.match.ID.test(parts[0])&&!Expr.match.ID.test(parts[parts.length-1])) {var ret=Sizzle.find(parts.shift(),context,contextXML);context=ret.expr?Sizzle.filter(ret.expr,ret.set)[0]:ret.set[0];} if(context) {var ret=seed?{expr:parts.pop(),set:makeArray(seed)}:Sizzle.find(parts.pop(),parts.length===1&&(parts[0]==="~"||parts[0]==="+")&&context.parentNode?context.parentNode:context,contextXML);set=ret.expr?Sizzle.filter(ret.expr,ret.set):ret.set;if(parts.length>0) {checkSet=makeArray(set);} else {prune=false;} while(parts.length) {var cur=parts.pop(),pop=cur;if(!Expr.relative[cur]) {cur="";} else {pop=parts.pop();} if(pop==null) {pop=context;} Expr.relative[cur](checkSet,pop,contextXML);}} else {checkSet=parts=[];}} if(!checkSet) {checkSet=set;} if(!checkSet) {throw"Syntax error, unrecognized expression: "+(cur||selector);} if(toString.call(checkSet)==="[object Array]") {if(!prune) {results.push.apply(results,checkSet);} else if(context&&context.nodeType===1) {for(var i=0;checkSet[i]!=null;i++) {if(checkSet[i]&&(checkSet[i]===true||checkSet[i].nodeType===1&&contains(context,checkSet[i]))) {results.push(set[i]);}}} else {for(var i=0;checkSet[i]!=null;i++) {if(checkSet[i]&&checkSet[i].nodeType===1) {results.push(set[i]);}}}} else {makeArray(checkSet,results);} if(extra) {Sizzle(extra,origContext,results,seed);Sizzle.uniqueSort(results);} return results;};Sizzle.uniqueSort=function(results) {if(sortOrder) {hasDuplicate=false;results.sort(sortOrder);if(hasDuplicate) {for(var i=1;i<results.length;i++) {if(results[i]===results[i-1]) {results.splice(i--,1);}}}}};Sizzle.matches=function(expr,set) {return Sizzle(expr,null,null,set);};Sizzle.find=function(expr,context,isXML) {var set,match;if(!expr) {return[];} for(var i=0,l=Expr.order.length;i<l;i++) {var type=Expr.order[i],match;if((match=Expr.match[type].exec(expr))) {var left=RegExp.leftContext;if(left.substr(left.length-1)!=="\\") {match[1]=(match[1]||"").replace(/\\/g,"");set=Expr.find[type](match,context,isXML);if(set!=null) {expr=expr.replace(Expr.match[type],"");break;}}}} if(!set) {set=context.getElementsByTagName("*");} return{set:set,expr:expr};};Sizzle.filter=function(expr,set,inplace,not) {var old=expr,result=[],curLoop=set,match,anyFound,isXMLFilter=set&&set[0]&&isXML(set[0]);while(expr&&set.length) {for(var type in Expr.filter) {if((match=Expr.match[type].exec(expr))!=null) {var filter=Expr.filter[type],found,item;anyFound=false;if(curLoop==result) {result=[];} if(Expr.preFilter[type]) {match=Expr.preFilter[type](match,curLoop,inplace,result,not,isXMLFilter);if(!match) {anyFound=found=true;} else if(match===true) {continue;}} if(match) {for(var i=0;(item=curLoop[i])!=null;i++) {if(item) {found=filter(item,match,i,curLoop);var pass=not^!!found;if(inplace&&found!=null) {if(pass) {anyFound=true;} else {curLoop[i]=false;}} else if(pass) {result.push(item);anyFound=true;}}}} if(found!==undefined) {if(!inplace) {curLoop=result;} expr=expr.replace(Expr.match[type],"");if(!anyFound) {return[];} break;}}} if(expr==old) {if(anyFound==null) {throw"Syntax error, unrecognized expression: "+expr;} else {break;}} old=expr;} return curLoop;};var Expr=Sizzle.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,NAME:/\[name=[\'\"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)[\'\"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*([\'\"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\(([\'\"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(elem) {return elem.getAttribute("href");}},relative:{"+":function(checkSet,part,isXML) {var isPartStr=typeof part==="string",isTag=isPartStr&&!/\W/.test(part),isPartStrNotTag=isPartStr&&!isTag;if(isTag&&!isXML) {part=part.toUpperCase();} for(var i=0,l=checkSet.length,elem;i<l;i++) {if((elem=checkSet[i])) {while((elem=elem.previousSibling)&&elem.nodeType!==1){} checkSet[i]=isPartStrNotTag||elem&&elem.nodeName===part?elem||false:elem===part;}} if(isPartStrNotTag) {Sizzle.filter(part,checkSet,true);}},">":function(checkSet,part,isXML) {var isPartStr=typeof part==="string";if(isPartStr&&!/\W/.test(part)) {part=isXML?part:part.toUpperCase();for(var i=0,l=checkSet.length;i<l;i++) {var elem=checkSet[i];if(elem) {var parent=elem.parentNode;checkSet[i]=parent.nodeName===part?parent:false;}}} else {for(var i=0,l=checkSet.length;i<l;i++) {var elem=checkSet[i];if(elem) {checkSet[i]=isPartStr?elem.parentNode:elem.parentNode===part;}} if(isPartStr) {Sizzle.filter(part,checkSet,true);}}},"":function(checkSet,part,isXML) {var doneName=done++,checkFn=dirCheck;if(!part.match(/\W/)) {var nodeCheck=part=isXML?part:part.toUpperCase();checkFn=dirNodeCheck;} checkFn("parentNode",part,doneName,checkSet,nodeCheck,isXML);},"~":function(checkSet,part,isXML) {var doneName=done++,checkFn=dirCheck;if(typeof part==="string"&&!part.match(/\W/)) {var nodeCheck=part=isXML?part:part.toUpperCase();checkFn=dirNodeCheck;} checkFn("previousSibling",part,doneName,checkSet,nodeCheck,isXML);}},find:{ID:function(match,context,isXML) {if(typeof context.getElementById!=="undefined"&&!isXML) {var m=context.getElementById(match[1]);return m?[m]:[];}},NAME:function(match,context,isXML) {if(typeof context.getElementsByName!=="undefined") {var ret=[],results=context.getElementsByName(match[1]);for(var i=0,l=results.length;i<l;i++) {if(results[i].getAttribute("name")===match[1]) {ret.push(results[i]);}} return ret.length===0?null:ret;}},TAG:function(match,context) {return context.getElementsByTagName(match[1]);}},preFilter:{CLASS:function(match,curLoop,inplace,result,not,isXML) {match=" "+match[1].replace(/\\/g,"")+" ";if(isXML) {return match;} for(var i=0,elem;(elem=curLoop[i])!=null;i++) {if(elem) {if(not^(elem.className&&(" "+elem.className+" ").indexOf(match)>=0)) {if(!inplace) result.push(elem);} else if(inplace) {curLoop[i]=false;}}} return false;},ID:function(match) {return match[1].replace(/\\/g,"");},TAG:function(match,curLoop) {for(var i=0;curLoop[i]===false;i++){} return curLoop[i]&&isXML(curLoop[i])?match[1]:match[1].toUpperCase();},CHILD:function(match) {if(match[1]=="nth") {var test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(match[2]=="even"&&"2n"||match[2]=="odd"&&"2n+1"||!/\D/.test(match[2])&&"0n+"+match[2]||match[2]);match[2]=(test[1]+(test[2]||1))-0;match[3]=test[3]-0;} match[0]=done++;return match;},ATTR:function(match,curLoop,inplace,result,not,isXML) {var name=match[1].replace(/\\/g,"");if(!isXML&&Expr.attrMap[name]) {match[1]=Expr.attrMap[name];} if(match[2]==="~=") {match[4]=" "+match[4]+" ";} return match;},PSEUDO:function(match,curLoop,inplace,result,not) {if(match[1]==="not") {if(match[3].match(chunker).length>1||/^\w/.test(match[3])) {match[3]=Sizzle(match[3],null,null,curLoop);} else {var ret=Sizzle.filter(match[3],curLoop,inplace,true^not);if(!inplace) {result.push.apply(result,ret);} return false;}} else if(Expr.match.POS.test(match[0])||Expr.match.CHILD.test(match[0])) {return true;} return match;},POS:function(match) {match.unshift(true);return match;}},filters:{enabled:function(elem) {return elem.disabled===false&&elem.type!=="hidden";},disabled:function(elem) {return elem.disabled===true;},checked:function(elem) {return elem.checked===true;},selected:function(elem) {elem.parentNode.selectedIndex;return elem.selected===true;},parent:function(elem) {return!!elem.firstChild;},empty:function(elem) {return!elem.firstChild;},has:function(elem,i,match) {return!!Sizzle(match[3],elem).length;},header:function(elem) {return/h\d/i.test(elem.nodeName);},text:function(elem) {return"text"===elem.type;},radio:function(elem) {return"radio"===elem.type;},checkbox:function(elem) {return"checkbox"===elem.type;},file:function(elem) {return"file"===elem.type;},password:function(elem) {return"password"===elem.type;},submit:function(elem) {return"submit"===elem.type;},image:function(elem) {return"image"===elem.type;},reset:function(elem) {return"reset"===elem.type;},button:function(elem) {return"button"===elem.type||elem.nodeName.toUpperCase()==="BUTTON";},input:function(elem) {return/input|select|textarea|button/i.test(elem.nodeName);}},setFilters:{first:function(elem,i) {return i===0;},last:function(elem,i,match,array) {return i===array.length-1;},even:function(elem,i) {return i%2===0;},odd:function(elem,i) {return i%2===1;},lt:function(elem,i,match) {return i<match[3]-0;},gt:function(elem,i,match) {return i>match[3]-0;},nth:function(elem,i,match) {return match[3]-0==i;},eq:function(elem,i,match) {return match[3]-0==i;}},filter:{PSEUDO:function(elem,match,i,array) {var name=match[1],filter=Expr.filters[name];if(filter) {return filter(elem,i,match,array);} else if(name==="contains") {return(elem.textContent||elem.innerText||"").indexOf(match[3])>=0;} else if(name==="not") {var not=match[3];for(var i=0,l=not.length;i<l;i++) {if(not[i]===elem) {return false;}} return true;}},CHILD:function(elem,match) {var type=match[1],node=elem;switch(type) {case'only':case'first':while(node=node.previousSibling) {if(node.nodeType===1)return false;} if(type=='first')return true;node=elem;case'last':while(node=node.nextSibling) {if(node.nodeType===1)return false;} return true;case'nth':var first=match[2],last=match[3];if(first==1&&last==0) {return true;} var doneName=match[0],parent=elem.parentNode;if(parent&&(parent.sizcache!==doneName||!elem.nodeIndex)) {var count=0;for(node=parent.firstChild;node;node=node.nextSibling) {if(node.nodeType===1) {node.nodeIndex=++count;}} parent.sizcache=doneName;} var diff=elem.nodeIndex-last;if(first==0) {return diff==0;} else {return(diff%first==0&&diff/first>=0);}}},ID:function(elem,match) {return elem.nodeType===1&&elem.getAttribute("id")===match;},TAG:function(elem,match) {return(match==="*"&&elem.nodeType===1)||elem.nodeName===match;},CLASS:function(elem,match) {return(" "+(elem.className||elem.getAttribute("class"))+" ").indexOf(match)>-1;},ATTR:function(elem,match) {var name=match[1],result=Expr.attrHandle[name]?Expr.attrHandle[name](elem):elem[name]!=null?elem[name]:elem.getAttribute(name),value=result+"",type=match[2],check=match[4];return result==null?type==="!=":type==="="?value===check:type==="*="?value.indexOf(check)>=0:type==="~="?(" "+value+" ").indexOf(check)>=0:!check?value&&result!==false:type==="!="?value!=check:type==="^="?value.indexOf(check)===0:type==="$="?value.substr(value.length-check.length)===check:type==="|="?value===check||value.substr(0,check.length+1)===check+"-":false;},POS:function(elem,match,i,array) {var name=match[2],filter=Expr.setFilters[name];if(filter) {return filter(elem,i,match,array);}}}};var origPOS=Expr.match.POS;for(var type in Expr.match) {Expr.match[type]=new RegExp(Expr.match[type].source+/(?![^\[]*\])(?![^\(]*\))/.source);} var makeArray=function(array,results) {array=Array.prototype.slice.call(array);if(results) {results.push.apply(results,array);return results;} return array;};try {Array.prototype.slice.call(document.documentElement.childNodes);}catch(e) {makeArray=function(array,results) {var ret=results||[];if(toString.call(array)==="[object Array]") {Array.prototype.push.apply(ret,array);} else {if(typeof array.length==="number") {for(var i=0,l=array.length;i<l;i++) {ret.push(array[i]);}} else {for(var i=0;array[i];i++) {ret.push(array[i]);}}} return ret;};} var sortOrder;if(document.documentElement.compareDocumentPosition) {sortOrder=function(a,b) {var ret=a.compareDocumentPosition(b)&4?-1:a===b?0:1;if(ret===0) {hasDuplicate=true;} return ret;};} else if("sourceIndex"in document.documentElement) {sortOrder=function(a,b) {var ret=a.sourceIndex-b.sourceIndex;if(ret===0) {hasDuplicate=true;} return ret;};} else if(document.createRange) {sortOrder=function(a,b) {var aRange=a.ownerDocument.createRange(),bRange=b.ownerDocument.createRange();aRange.selectNode(a);aRange.collapse(true);bRange.selectNode(b);bRange.collapse(true);var ret=aRange.compareBoundaryPoints(Range.START_TO_END,bRange);if(ret===0) {hasDuplicate=true;} return ret;};} (function() {var form=document.createElement("div"),id="script"+(new Date).getTime();form.innerHTML="<a name='"+id+"'/>";var root=document.documentElement;root.insertBefore(form,root.firstChild);if(!!document.getElementById(id)) {Expr.find.ID=function(match,context,isXML) {if(typeof context.getElementById!=="undefined"&&!isXML) {var m=context.getElementById(match[1]);return m?m.id===match[1]||typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id").nodeValue===match[1]?[m]:undefined:[];}};Expr.filter.ID=function(elem,match) {var node=typeof elem.getAttributeNode!=="undefined"&&elem.getAttributeNode("id");return elem.nodeType===1&&node&&node.nodeValue===match;};} root.removeChild(form);})();(function() {var div=document.createElement("div");div.appendChild(document.createComment(""));if(div.getElementsByTagName("*").length>0) {Expr.find.TAG=function(match,context) {var results=context.getElementsByTagName(match[1]);if(match[1]==="*") {var tmp=[];for(var i=0;results[i];i++) {if(results[i].nodeType===1) {tmp.push(results[i]);}} results=tmp;} return results;};} div.innerHTML="<a href='#'></a>";if(div.firstChild&&typeof div.firstChild.getAttribute!=="undefined"&&div.firstChild.getAttribute("href")!=="#") {Expr.attrHandle.href=function(elem) {return elem.getAttribute("href",2);};}})();if(document.querySelectorAll)(function() {var oldSizzle=Sizzle,div=document.createElement("div");div.innerHTML="<p class='TEST'></p>";if(div.querySelectorAll&&div.querySelectorAll(".TEST").length===0) {return;} Sizzle=function(query,context,extra,seed) {context=context||document;if(!seed&&context.nodeType===9&&!isXML(context)) {try {return makeArray(context.querySelectorAll(query),extra);}catch(e){}} return oldSizzle(query,context,extra,seed);};for(var prop in oldSizzle) {Sizzle[prop]=oldSizzle[prop];}})();if(document.getElementsByClassName&&document.documentElement.getElementsByClassName)(function() {var div=document.createElement("div");div.innerHTML="<div class='test e'></div><div class='test'></div>";if(div.getElementsByClassName("e").length===0) return;div.lastChild.className="e";if(div.getElementsByClassName("e").length===1) return;Expr.order.splice(1,0,"CLASS");Expr.find.CLASS=function(match,context,isXML) {if(typeof context.getElementsByClassName!=="undefined"&&!isXML) {return context.getElementsByClassName(match[1]);}};})();function dirNodeCheck(dir,cur,doneName,checkSet,nodeCheck,isXML) {var sibDir=dir=="previousSibling"&&!isXML;for(var i=0,l=checkSet.length;i<l;i++) {var elem=checkSet[i];if(elem) {if(sibDir&&elem.nodeType===1) {elem.sizcache=doneName;elem.sizset=i;} elem=elem[dir];var match=false;while(elem) {if(elem.sizcache===doneName) {match=checkSet[elem.sizset];break;} if(elem.nodeType===1&&!isXML) {elem.sizcache=doneName;elem.sizset=i;} if(elem.nodeName===cur) {match=elem;break;} elem=elem[dir];} checkSet[i]=match;}}} function dirCheck(dir,cur,doneName,checkSet,nodeCheck,isXML) {var sibDir=dir=="previousSibling"&&!isXML;for(var i=0,l=checkSet.length;i<l;i++) {var elem=checkSet[i];if(elem) {if(sibDir&&elem.nodeType===1) {elem.sizcache=doneName;elem.sizset=i;} elem=elem[dir];var match=false;while(elem) {if(elem.sizcache===doneName) {match=checkSet[elem.sizset];break;} if(elem.nodeType===1) {if(!isXML) {elem.sizcache=doneName;elem.sizset=i;} if(typeof cur!=="string") {if(elem===cur) {match=true;break;}} else if(Sizzle.filter(cur,[elem]).length>0) {match=elem;break;}} elem=elem[dir];} checkSet[i]=match;}}} var contains=document.compareDocumentPosition?function(a,b) {return a.compareDocumentPosition(b)&16;}:function(a,b) {return a!==b&&(a.contains?a.contains(b):true);};var isXML=function(elem) {return elem.nodeType===9&&elem.documentElement.nodeName!=="HTML"||!!elem.ownerDocument&&elem.ownerDocument.documentElement.nodeName!=="HTML";};var posProcess=function(selector,context) {var tmpSet=[],later="",match,root=context.nodeType?[context]:context;while((match=Expr.match.PSEUDO.exec(selector))) {later+=match[0];selector=selector.replace(Expr.match.PSEUDO,"");} selector=Expr.relative[selector]?selector+"*":selector;for(var i=0,l=root.length;i<l;i++) {Sizzle(selector,root[i],tmpSet);} return Sizzle.filter(later,tmpSet);};window.Sizzle=Sizzle;})();

/**
 * Selects a single HTML element, using a css expression and executed through Sizzle.
 * @see http://sizzlejs.com/
 * @param {String} expression The css selection expression.
 * @param {HTMLElement} context The element from which to execute selection. If omitted, it will default to the current document.
 * @return {HTMLElement} The selected HTML element.
 */
function $(expression, context)
{
	return $$(expression, context)[0];
}

/**
 * Selects an array of HTML elements, using a css expression and executed through Sizzle.
 * @see http://sizzlejs.com/
 * @param {String} expression The css selection expression.
 * @param {HTMLElement} context The element from which to execute selection. If omitted, it will default to the current document.
 * @return {HTMLElement} An array of selected HTML elements.
 */
function $$(expression, context)
{
	var result = Sizzle(expression, context || document);
	return result;
}

/**
 * Provides utility methods for working with javascript types.
 */
function Type()
{
}

/**
 * Registers the supplied namespace.
 * @param {String} namespace The string that specifies the namespace to register, eg: Aeon.Utils.Types
 */
Type.registerNamespace = function Type$registerNamespace(namespace)
{
	var parts = namespace.split(".");
	var context = window;
	for (var i = 0; i < parts.length; i++)
	{
		if (context[parts[i]] == null)
			context[parts[i]] = {};

		context = context[parts[i]];
	}
}

/**
 * Returns <c>true</c> if the specified object is an array.
 * @param {Object} value The value to test.
 * @return {Boolean} <c>true</c> if the specified object is an array, otherwise <c>false</c>.
 */
Type.isArray = function Type$isArray(value)
{
	if (value == null)
		return false;
	if (value.isArray)
		return true;

	return (
		(value.constructor == Array) ||
		(value != window && !Type.isString(value) && Type.isNumber(value.length))
	);
}

/**
 * Returns <c>true</c> if the specified object is a boolean.
 * @param {Object} value The value to test.
 * @return {Boolean} <c>true</c> if the specified object is a boolean, otherwise <c>false</c>.
 */
Type.isBoolean = function Type$isBoolean(value)
{
	if (value == null)
		return false;

	return (
		(typeof value == 'boolean') ||
		(value.constructor == Boolean)
	);
}

/**
 * Returns <c>true</c> if the specified object is a function.
 * @param {Object} value The value to test.
 * @return {Boolean} <c>true</c> if the specified object is a function, otherwise <c>false</c>.
 */
Type.isFunction = function Type$isFunction(value)
{
	if (value == null)
		return false;

	return (
		(typeof value == 'function') ||
		(value.constructor == Function)
	);
}

/**
 * Returns <c>true</c> if the specified object is a null.
 * @param {Object} value The value to test.
 * @return {Boolean} <c>true</c> if the specified object is a null, otherwise <c>false</c>.
 */
Type.isNull = function Type$isNull(value)
{
	return (value == null || value == undefined);
}

/**
 * Returns <c>true</c> if the specified object is a number.
 * @param {Object} value The value to test.
 * @return {Boolean} <c>true</c> if the specified object is a number, otherwise <c>false</c>.
 */
Type.isNumber = function Type$isNumber(value)
{
	if (value == null)
		return false;

	return (
		(typeof value == 'number' && isFinite(value)) ||
		(value.constructor == Number)
	);
}

/**
 * Returns <c>true</c> if the specified object can be converted to a number.
 * @param {Object} value The value to test.
 * @return {Boolean} <c>true</c> if the specified object can be converted to a number, otherwise <c>false</c>.
 */
Type.isNumeric = function Type$isNumeric(value)
{
	return !isNaN(value);
}

/**
 * Returns <c>true</c> if the specified object is a date.
 * @param {Object} value The value to test.
 * @return {Boolean} <c>true</c> if the specified object is a date, otherwise <c>false</c>.
 */
Type.isDate = function Type$isDate(value)
{
	if (value == null)
		return false;
	if (value.isDate)
		return true;

	return (
		(Type.isFunction(value.getDate) && Type.isFunction(value.getTime))
	);
}

/**
 * Returns <c>true</c> if the specified object is an object.
 * @param {Object} value The value to test.
 * @return {Boolean} <c>true</c> if the specified object is an object, otherwise <c>false</c>.
 */
Type.isObject = function Type$isObject(value)
{
	if (value == null)
		return false;

	return (
		typeof value == 'object' &&
		value.constructor != String &&
		value.constructor != Number) || Type.isFunction(value); /**/
}

/**
 * Returns <c>true</c> if the specified object is a string.
 * @param {Object} value The value to test.
 * @return {Boolean} <c>true</c> if the specified object is a string, otherwise <c>false</c>.
 */
Type.isString = function Type$isString(value)
{
	if (value == null)
		return false;

	return (
		typeof value == 'string' ||
		(value != null && value.constructor == String)
	);
}

/**
 * Returns <c>true</c> if the specified object is undefined.
 * @param {Object} value The value to test.
 * @return {Boolean} <c>true</c> if the specified object is undefined, otherwise <c>false</c>.
 */
Type.isUndefined = function Type$isUndefined(value)
{
	return (typeof value == 'undefined');
}

/**
 * Returns <c>true</c> if the specified value is an HTML or XML node.
 * @param {Object} value The value to test.
 * @return {Boolean} <c>true</c> if the specified object is an XML or HTML node, otherwise <c>false</c>.
 */
Type.isNode = function Type$isNode(value)
{
	return (Type.isObject(value) && Type.isNumber(value.nodeType));
}

/**
 * Returns <c>true</c> if the specified value is an HTML or XML document.
 * @param {Object} value The value to test.
 * @return {Boolean} <c>true</c> if the specified object is an HTML or XML document, otherwise <c>false</c>.
 */
Type.isDocument = function Type$isDocument(value)
{
	return (Type.isNode(value) && value.nodeType == 9);
}

/**
 * Returns <c>true</c> if the specified value is an XML or HTML element.
 * @param {Object} value The object to test.
 * @return {Boolean} <c>true</c> if the specified object is an XML or HTML element, otherwise <c>false</c>.
 */
Type.isElement = function Type$isElement(value)
{
	return (Type.isNode(value) && value.nodeType == 1);
}

/**
 * Returns <c>true</c> if the specified object is an HTML element.
 * @param {Object} object The object to test.
 * @return {Boolean} <c>true</c> if the specified object is an HTML element, otherwise <c>false</c>.
 */
Type.isHtmlElement = function Type$isHtmlElement(object)
{
	return (Type.isElement(object) && Type.isString(object.className));
}

/**
 * Returns <c>true</c> if the specified object is an instance of the specified type.
 * @param {Object} object The object to test.
 * @param {Function} type The type to test against.
 * @return {Boolean} <c>true</c> if the specified object is an instance of the specified type, otherwise <c>false</c>.
 */
Type.instanceOf = function Type$instanceOf(object, type)
{
	if (!Type.isObject(object) || !Type.isFunction(type))
		return false;

	if (object instanceof type)
		return true;

	var result = false;
	if (object.___ && object.___.constructor == type)
		return true;

	if (object.___ && object.___.base != null)
	{
		var parent = object;
		while (parent = parent.___.base)
		{
			if (parent.___.constructor == type)
				return true;
		}
	}
	return false;
}

/**
 * Returns <c>true</c> if the specified object implements the specified interface.
 * @param {Object} object The object to test.
 * @return {Boolean} <c>true</c> if the specified object implements the specified interface, otherwise <c>false</c>.
 */
Type.implements = function Type$implements(object, interface)
{
	var result = true;
	if (object == null || interface == null)
		result = false;

	else
	{
		for (var name in interface)
		{
			if (!Type.isFunction(object[name]))
			{
				result = false;
				break;
			}
		}
	}
	return result;
}

Array.prototype.isArray = true;

/**
 * Returns an array of arguments.
 * The first argument is the array of arguments that was passed to the calling function. The second argument is the start index
 * at which to check for an element that is itself an array, or from which to start copying elements into the resulting list. If the
 * <c>start</c> argument is omitted, it will default to zero. Therefore, if the Nth element in the list is itself an array, it will
 * be that element that will be returned.
 * @example var args = Array.fromArguments(arguments);
 * @example var args = Array.fromArguments(arguments, 2);
 * @param {Array} list The array of arguments from which to return a list
 * @param {Number} start The index from which to start copying. Optional (Default is 0).
 * @param {Number} stop The index at which to stop copying. Optional (Default is list.length).
 * @return {Array} The array of arguments.
 */
Array.fromArguments = function Array$fromArguments(list, start, stop)
{
	if (isNaN(parseInt(start)))
		start = 0;

	var result = [];
	if (list && Type.isArray(list[start]))
	{
		result = list[start];
	}
	else if (list)
	{
		if (isNaN(parseInt(stop)))
			stop = list.length - 1;

		for (var i = start; i <= stop; i++)
		{
			result.push(list[i]);
		}
	}

	return result;
}

/**
 * Property that helps with identifying arrays when doing type checking.
 * @type {Boolean}
 */
Array.prototype.isArray = true;

/**
 * Returns a value indicating whether the specified value exists in the current array.
 * @param {Object} value The value to find
 * @returns {Boolean} <c>true</c> if the value was found, otherwise <c>false</c>.
 */
Array.prototype.contains = function Array$contains(value)
{
	return this.indexOf(value) != -1;
}

/**
 * Returns the index of the specifid value. If the value wasn't found, returns -1.
 * @param {Object} value The value to find
 * @returns {Number} The index of the specified value within this array.
 */
Array.prototype.indexOf = function Array$indexOf(value)
{
	var result = -1;
	for (var i = 0; i < this.length; i++)
	{
		if (this[i] === value)
		{
			result = i;
			break;
		}
	}
	return result;
}

/**
 * Returns a new array with the specified values removed from it
 * @param {String} value The values to remove (can be any number or arguments)
 * @return {Array} A new array with the value removed from it
 */
Array.prototype.remove = function Array$remove()
{
	var list = new Array;
	var remove = false;
	for (var i = this.length - 1; i >= 0; i--)
	{
		remove = false;
		for (var j = 0; j < arguments.length; j++)
		{
			if (this[i] == arguments[j])
			{
				remove = true;
				break;
			}
		}
		if (remove == true)
			list.push(i);
	}
	for (var i = 0; i < list.length; i++)
	{
		this.splice(list[i], 1);
	}

	return this;
}

/**
 * Returns a sum of all elements in the array.
 * @return {Number} The sum of all values in the array.
 */
Array.prototype.sum = function Array$sum()
{
	var result = 0;
	for (var i = 0; i < this.length; i++)
		result += Number(this[i]) || 0;

	return result;
}

/**
 * Calls the supplied function on each of the elements in the array.
 * @param {Function} fx The function to call.
 * @param {Object} context The context object in which to call the function. Optional.
 */
Array.prototype.each = function Array$each(fx, context)
{
	for (var i = 0; i < this.length; i++)
	{
		var r = fx.call(context, this[i], i, this);
		if (r != undefined)
			this[i] = r;
	}

	return this;
}

Number.random = function Number$random(randTop)
{
	var top = Math.abs(randTop);
	var random = Math.round((top) * Math.random());
	return random;
}

Date.prototype.isDate = true;

Date.today = function Date$today()
{
	var now = new Date();
	var today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
	return today;
}

Date._parse = Date.parse;

Date.parse = function Date$parse(dateValue)
{
	var date = null;

	if (dateValue == null)
		return null;

	else if (dateValue instanceof Date)
		return dateValue.getTime();

	else if (dateValue instanceof Array)
	{
		if (dateValue.length == 3)
		{
			var currentDate = new Date(dateValue[2], dateValue[1]-1, dateValue[0]);
			if (isNaN(currentDate))
				return null;
			else
				return currentDate.getTime();
		}
		else
			return null;
	}
	else if ((date = Date._parse(dateValue)))
	{
		return date;
	}
	else
	{
		var dateString = new String(dateValue);
		if (dateString.length == 0)
			return null;

		// xsl-short: YYYY-MM-DD or general date: YYYY/MM/DD
		var result;
		if ((result = dateString.match(/^(\d{4})(?:-|\/)(\d{1,2})(?:-|\/)(\d{1,2})$/)))
			date = new Date(result[1], result[2]-1, result[3]);
		// xsl-long:  YYYY-MM-DDTHH:NN:SS
		else if ((result = dateString.match(/^(\d{4})-(\d{1,2})-(\d{1,2})T(\d{1,2}):(\d{1,2}):(\d{1,2})/)))
			date = new Date(result[1], result[2]-1, result[3], result[4], result[5], result[6]);
		// js-string: www MMM dd hh:mm:ss ZON+dddd yyyy
		else if (dateString.match(/^\w{3}\s\w{2}\s\d{1,2}\s\d{2}:\d{2}:\d{2}\s\w{3}\+\d{4}\s\d{4}$/))
			date = new Date(dateString);
		// js-milliseconds date
		else if (dateString.match(/^-?\d+$/))
			date = new Date(parseInt(dateString));
		// lets generalize a bit: [dd-mm-yyyy] or [dd/mm/yyyy] or [dd.mm.yyyy]
		else if ((result = dateString.match(/^(\d{1,2})[\.\/\-](\d{1,2})[\.\/\-](\d{4})$/)))
			date = new Date(result[3], result[2]-1, result[1]);
		// more generalization:  [yyyy-mm-dd] or [yyyy.mm.dd] or [yyyy/mm/dd]
		else if ((result = dateString.match(/^(\d{4})[\.\/\-](\d{1,2})[\.\/\-](\d{1,2})$/)))
			date = new Date(result[1], result[2]-1, result[3]);
		// and some more: [dd-mm-yyyy hh:nn:ss] or [dd/mm/yyyy hh:nn:ss] or [dd.mm.yyyy hh:nn:ss]
		else if ((result = dateString.match(/^(\d{1,2})[\.\/\-](\d{1,2})[\.\/\-](\d{4}) (\d{1,2}):(\d{1,2})(?::(\d{1,2}))?/)))
			date = new Date(result[3], result[2]-1, result[1], result[4], result[5], result[6] || 0);
		// and more:  [yyyy-mm-dd hh:nn:ss] or [yyyy.mm.dd hh:nn:ss] or [yyyy/mm/dd hh:nn:ss]
		else if ((result = dateString.match(/^(\d{4})[\.\/\-](\d{1,2})[\.\/\-](\d{1,2}) (\d{1,2}):(\d{1,2})(?::(\d{1,2}))?$/)))
			date = new Date(result[1], result[2]-1, result[3], result[4], result[5], result[6] || 0);

		return date;
	}
}

Date.offset = function Date$offset(dateInstance, timeFormat)
{
	var timeMultiplier =  { s: 1000, n: 1000*60, h: 1000*60*60, d: 1000*60*60*24, m: 1000*60*60*24*30, y: 1000*60*60*24*365 }
	var timeOffset = 0;

	if (timeFormat != undefined)
		if (String(timeFormat).match( /^([+-]?(?:\d+|\d*\.\d*))([mhdMy]?)$/ ))
			timeOffset = parseInt(timeMultiplier[RegExp.$2] * RegExp.$1);

	dateInstance.setTime(dateInstance.getTime() + timeOffset);
	return dateInstance;
}

Date.DayNames = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
Date.MonthNames = ["January","February","March","April","May","June","July","August","September","October","November","December"];
Date.AM = "AM";
Date.PM = "PM";

Date.prototype.getWeekDay = function Date$getWeekDay()
{
	return Date.DayNames[this.getDay()];
}

Date.prototype._toString = Date.prototype.toString;

Date.prototype.toString = function Date$toString(format)
{
	if (isNaN(this))
		return "NaN";

	if (!Type.isString(format))
		return this._toString();

	var date = this;
	return format.replace(/\Wss?|hh?|nn?|dd?|wwww?|mmmm|mmm|mm|m|yyyy|yy|y\W/g, function Date$toString$format() /**/
	{
		switch (arguments[0])
		{
			case    "s": return date.getSeconds();
			case   "ss": return date.getSeconds().toString().padLeft(2, 0);
			case    "n": return date.getMinutes();
			case   "nn": return date.getMinutes().toString().padLeft(2, 0);
			case    "h": return date.getHours();
			case   "hh": return date.getHours().toString().padLeft(2, 0);
			case    "m": return (date.getMonth() + 1);
			case   "mm": return (date.getMonth() + 1).toString().padLeft(2, 0);
			case  "mmm": return Date.MonthNames[date.getMonth()].substr(0, 3);
			case "mmmm": return Date.MonthNames[date.getMonth()];
			case    "d": return date.getDate();
			case   "dd": return date.getDate().toString().padLeft(2, 0);
			case  "www": return Date.DayNames[date.getDay()].substr(0, 3);
			case "wwww": return Date.DayNames[date.getDay()];
			case    "y": return date.getFullYear().toString().substr(2, 4);
			case   "yy": return date.getFullYear().toString().substr(2, 4);
			case "yyyy": return date.getFullYear();
		}
		return arguments[0];
	});
}

Date.prototype.offset = function Date$offset(timeFormat)
{
	return Date.offset(this, timeFormat);
}

Date.prototype.getUTCTime = function Date$getUTCTime()
{
	return this.getTime() + (this.getTimezoneOffset() * 60000)
}

Function.prototype.isFunction = true;

/**
 * Returns a previously created delegate.
 * @param {Object} object The target of the delegate.
 * @param {Object} method The function of the delegate.
 * @param {Array} fxargs The arguments that should be used when applying the delegate function. Optional.
 * @return {Function} The delegate function.
 */
Function.getDelegate = function Function$getDelegate(object, method, fxargs)
{
	if (Function.callbacks == null)
		Function.callbacks = [];

	if (Type.isString(method))
		method = object[method];

	for (var i = 0; i < Function.callbacks.length; i++)
	{
		var cb = Function.callbacks[i];
		var fx = true;
		if (cb.fxargs && fxargs)
			for (var j = 0; j < cb.fxargs.length; j++)
				if (cb.fxargs[j] != fxargs[j])
					fx = false;

		if (cb.object == object && cb.method == method && fx == true)
			return cb;
	}

	return null;
}

/**
 * Creates a function that will be execute the specifed method in the context of the specified object.
 * @param {Object} object The object on which the method will be applied.
 * @param {Object} method The method that should be applied on the object. This can be either a direct reference to the method or
 * a string name of the method of the specified object.
 * @param {Array} fxargs The arguments that should be used when applying the method. Optional.
 * @return {Function} The delegate function.
 */
Function.createDelegate = function Function$createDelegate(object, method, fxargs)
{
	var callbackFx = Function.getDelegate(object, method, fxargs); /**/
	if (callbackFx != null)
		return callbackFx;

	if (Type.isString(method))
		method = object[method];

	if (!Type.isFunction(method))
	{
		$log.error("Method not found");
		return null;
	}

	callbackFx = function delegate() /**/
	{
		var object = arguments.callee.object;
		var method = arguments.callee.method;
		var fxargs = arguments.callee.fxargs;

		if (arguments.length)
			return method.apply(object, arguments);
		else if (fxargs)
			return method.apply(object, fxargs);
		else
			return method.apply(object);
	}
	callbackFx.object = object;
	callbackFx.method = method;
	callbackFx.fxargs = fxargs;

	Function.callbacks.push(callbackFx); /**/

	return callbackFx;
}

/**
 * Proxy function to <c>window.setInterval</c>.
 * The reason this has been created is to change the interface of <c>window.setInterval</c> so that the
 * function being assigned comes as the last argument, not the first.
 * <p>Documentation for <c>window.setInterval</c>: Calls a function repeatedly, with a fixed time delay between
 * each call to that function</p>
 * @param {Number} interval The time (in milliseconds) to wait between calling the supplied function.
 * @param {Function} callback The function to call periodically.
 * @return {Number} A unique interval ID you can pass to <c>window.clearInterval(intervalID)</c>.
 */
Function.setInterval = function Function$setInterval(interval, callback)
{
	return window.setInterval(callback, interval);
}

/**
 * Proxy function to <c>window.setTimeout</c>.
 * The reason this has been created is to change the interface of <c>window.setTimeout</c> so that the
 * function being assigned comes as the last argument, not the first.
 * <p>Documentation for <c>window.setTimeout</c>: Executes a code snippet or a function after specified delay.</p>
 * @param {Number} timeout The time (in milliseconds) to wait before calling the supplied function.
 * @param {Function} callback The function to call.
 * @return {Number} A unique timeout ID you can pass to <c>window.clearTimeout(timeoutID)</c>.
 */
Function.setTimeout = function Function$setTimeout(timeout, callback)
{
	return window.setTimeout(callback, timeout);
}

Function.prototype.getName = function Function$getName(includeArguments, argumentsOnly)
{
	var name = this.toString().replace(/^[\s\S]*?function[\s\n]+(([\w.$]*)[\s\n]*\((.*)\))\W[\s\S]*$/, function () /* rxp */
	{
		var fxName = arguments[2] ? arguments[2] : "anonymous";
		var functionDefinition = arguments[2] ? arguments[1] : [fxName, "(", arguments[3], ")"].join(String.EMPTY);

		if (!includeArguments && !argumentsOnly)
			return fxName;

		if (includeArguments && !argumentsOnly)
			return functionDefinition;

		if (includeArguments && argumentsOnly)
			return arguments[3];
	});

	return name.replace(/([\w\$])\$([\w\$])/g, "$1.$2");
}

Function.prototype.getStackTrace = function Function$getStackTrace(point, maxIterations)
{
	var fxCaller = point || arguments.callee.caller;
	var callStack = new Array();
	var functions = new Array();

	var currentCount = 0;
	while (fxCaller != null && fxCaller.getName)
	{
		if (maxIterations && currentCount >= maxIterations)
			break;

		// unfortunatelly this script goes into an infinite loop
		// if there is a recursion going on in the function call stack :(
		else if (functions.contains(fxCaller)) /**/
			break;

		var fxName = fxCaller.getName();
		var fxArgs = new Array();
		var argValues = fxCaller.arguments;

		for (var i = 0; i < argValues.length; i++)
		{
			if (Type.isFunction(argValues[i])) /**/
				fxArgs.push(argValues[i].getName(true));

			else if (Type.isArray(argValues[i]))
				fxArgs.push("array[{0}]".format(argValues[i].length));

			else if (Type.isObject(argValues[i]))
				fxArgs.push("object{..}");

			else if (Type.isNull(argValues[i]))
				fxArgs.push("null");

			else if (Type.isString(argValues[i]))
				fxArgs.push(argValues[i].length < 200
					? "\"{0}\"".format(argValues[i])
					: "[string({0})]".format(argValues[i].length));

			else if (Type.isUndefined(argValues[i]))
				fxArgs.push("undefined");

			else
				fxArgs.push(argValues[i]);
		}

		functions.push(fxCaller); /**/
		callStack.unshift(fxCaller.getName() + "(" + fxArgs.join(", ") + ")");

		try
		{
			fxCaller = fxCaller.caller;
		}
		catch(e)
		{
			break;
		}
		currentCount++;
	}
	callStack.toString = function (joinString) /**/
	{
		return this.join(joinString || "\n");
	}
	return callStack;
}

/**
 * Provides a simple inheritance mechanism that enables calling base methods from the derived types
 * @param {Function} ancestor The ancestor function that this function extends.
 */
Function.prototype.inherits = function Function$inherits(ancestor)
{
	var sourcePrototype = ancestor.prototype;
	var targetPrototype = this.prototype;

	targetPrototype.___ = { base: sourcePrototype, constructor: ancestor };

	for (var name in sourcePrototype)
	{
		if (targetPrototype[name] == null || Object.prototype[name] != null)
			targetPrototype[name] = sourcePrototype[name];
	}
	targetPrototype.$method = Function.prototype.$method;
	targetPrototype.$super = Function.prototype.$super;
}

/**
 * Applies the constructor of the base type to the current type.
 * @arguments {Object} 0-n The arguments that should be supplied to the base constructor.
 */
Function.prototype.$super = function Function$$super()
{
	if (this.$$ == null)
		this.$$ = this;

	var constructor = this.$$.___.constructor;
	try
	{
		if (constructor)
		{
			var callArgs = Array.fromArguments(arguments);
			this.$$ = this.$$.___.base;
			constructor.apply(this, arguments);
		}
	}
	finally
	{
		this.$$ = null;
	}
}

/**
 * Applies the specified method of the base type to the current type.
 * @param {String} method The name of the method to apply.
 * @arguments {Object} 1-n The arguments that should be supplied to the base method.
 * @return {Object} The value returned by the base method.
 */
Function.prototype.$method = function Function$$method(method)
{
	if (this.$$ == null)
		this.$$ = this;

	var base = this.$$.___.base;
	var result = undefined;
	try
	{
		if (base && Type.isFunction(base[method]))
		{
			var callArgs = Array.fromArguments(arguments, 1);
			this.$$ = this.$$.___.base;
			result = base[method].apply(this, callArgs);
		}
	}
	finally
	{
		this.$$ = null;
	}
	return result;
}

Object.serialize = function Object$serialize(object)
{
	if (Type.isNumber(object))
	{
		return object;
	}
	else if (Type.isString(object))
	{
		return Object.serializeString(object, true);
	}
	else if (Type.isArray(object))
	{
		return Object.serializeArray(object);
	}
	else if (Type.isDate(object))
	{
		return Object.serializeDate(object);
	}
	else if (Type.isFunction(object))
	{
		return Object.serializeFunction(object);
	}
	else if (Type.isObject(object))
	{
		return Object.serializeObject(object);
	}

	return String(object);
}

Object.serializeDate = function Object$serializeDate(value)
{
	if (value == null)
		return "null";

	return String.format("new Date({0})", value.getTime());
}

Object.serializeFunction = function Object$serializeFunction(value)
{
	return value.toString();
}

Object.serializeArray = function Object$serializeArray(value)
{
	if (value == null)
		return "null";

	var result = [];
	result.push("[");

	var values = [];
	for (var i = 0; i < value.length; i++)
		values.push(Object.serialize(value[i]));

	result.push(values.join(", "));
	result.push("]");

	return result.join(String.EMPTY);
}

Object.serializeObject = function Object$serializeObject(object)
{
	if (object == null)
		return "null";

	var result = [];
	result.push("{");

	var members = [];
	for (var name in object)
	{
		var member = [];
		member.push(Object.serializeString(name, true));
		member.push(": ");
		member.push(Object.serialize(object[name]));

		members.push(member.join(String.EMPTY));
	}
	result.push(members.join(", "));
	result.push("}");

	return result.join(String.EMPTY);
}

Object.serializeString = function Object$serializeString(value, quote)
{
	if (value == null)
		return "null";

	value = String(value).replace(
		/(^|[^\\])\"/g, "$1\\\"").replace(
		/(^|[^\\])\'/g, "$1\\'");

	return (quote ? '"' + value + '"' : value);
}

/**
 * Defines an empty string as a constant value.
 * @type {String}
 */
String.EMPTY = "";

/**
 * Defines the linebreak constant.
 * @type {String}
 */
String.LINEBREAK = "\r\n";

/**
 * Defines the tab constant.
 * @type {String}
 */
String.TAB = "\t";

/**
 * Replaces the specified format string with supplied arguments.
 * @param {String} value The format string.
 * @arguments {String} 1-n The values to replace the string with.
 * @return {String} The formatted version of the supplied string.
 */
String.format = function String$format(value)
{
	var args = Array.fromArguments(arguments, 1);

	function applyPadding(string, count, character, direction) /**/
	{
		var string = '' + string;
		var diff = count - string.length;
		var output = new String();
		while (output.length < diff)
			output += character;

		return (direction == 2 ? string + output : output + string);
	}
	function applyFormat() /**/
	{
		try
		{
			var value = new String(args[arguments[1]]);
		}
		catch(e) { value = String.EMPTY; }

		if (arguments[2] != String.EMPTY && arguments[3] != String.EMPTY)
		{
			var direction = arguments[4] == "+" ? 2 : 1;
			value = applyPadding(value, arguments[3], arguments[2] == "#" ? "0" : " ", direction);
		}
		return value;
	}
	if (String.RX_FORMAT == null)
		String.RX_FORMAT = /\{(\d+)([$#])?(\d+)?([+-])?\}/g;

	return String(value).replace(String.RX_FORMAT, applyFormat);
}

/**
 * Removes characters from the beginning and the end of the supplied string value.
 * @param {String} value The string to trim.
 * @param {String} expression The string specifying the regular expression that selects the characters to remove.
 * The default expression selects and removes any whitespace characters ([\s]*). Optional.
 * @return {String} The trimmed value.
 */
String.trim = function String$trim(value, expression)
{
	if (expression == null)
		expression = "\\s";

	var re = new RegExp("^[" + expression + "]*([\\s\\S]*?)[" + expression + "]*$");
	return String(value).replace(re, "$1");
}

/**
 * Replaces the current string with supplied arguments.
 * @arguments {String} 0-n The values to replace the string with.
 * @return {String} The formatted version of the current string.
 */
String.prototype.format = function String$format()
{
	return String.format(this, arguments);
}

/**
 * Prepends characters to the current string until it reaches the specified length.
 * @param {Number} length The final length that the string should be.
 * @param {String} padChar The character to pad the string with, default is ' '. Optional.
 * @return {String} The padded version of the current string.
 */
String.prototype.padLeft = function String$padLeft(length, padChar)
{
	if (padChar == undefined)
		padChar = " ";

	var string = this.toString();
	while(string.length < length)
		string = padChar + string;
	return string;
};

/**
 * Appends characters to the current string until it reaches the specified length.
 * @param {Number} length The final length that the string should be.
 * @param {String} padChar The character to pad the string with, default is ' '. Optional.
 * @return {String} The padded version of the current string.
 */
String.prototype.padRight = function String$padRight(length, padChar)
{
	if (padChar == undefined)
		padChar = " ";

	var string = this.toString();
	while(string.length < length)
		string = string + padChar;
	return string;
};

/**
 * Removes characters from the beginning and the end of the current string.
 * @param {String} expression The string specifying the regular expression that selects the characters to remove. The default expression
 * selected any whitespace characters. Optional.
 * @return {String} The trimmed version of the current string
 */
String.prototype.trim = function String$trim(expression)
{
	return String.trim(this, expression);
}

Type.registerNamespace("Aeon");

/**
 * Provides a container for various utility objects.
 */
Aeon.Utils = function Utils()
{
}

Aeon.Utils.Assert = new function Assert()
{
}

/**
 * Checks that value is <code>true</code>.
 * @param {Object} value The value to check.
 * @param {String} description Optional description of this assertion.
 * @return {Boolean} The result of assertion.
 */
Aeon.Utils.Assert.isTrue = function Assert$isTrue(value, description)
{
	var result = (value == true);
	if (result == false)
		Aeon.Utils.Assert.raiseError(description || ("Value should be true."));

	return result;
}

/**
 * Checks that value is <code>false</code>.
 * @param {Object} value The value to check.
 * @param {String} description Optional description of this assertion.
 * @return {Boolean} The result of assertion.
 */
Aeon.Utils.Assert.isFalse = function Assert$isFalse(value, description)
{
	var result = (value == false);
	if (result == false)
		Aeon.Utils.Assert.raiseError(description || ("Value should be false."));

	return result;
}

/**
 * Checks that value is defined.
 * @param {Object} value The value to check.
 * @param {String} description Optional description of this assertion.
 * @return {Boolean} The result of assertion.
 */
Aeon.Utils.Assert.isDefined = function Assert$isDefined(value, description)
{
	var result = (typeof(value) != 'undefined');
	if (result == false)
		Aeon.Utils.Assert.raiseError(description || ("Value should be defined."));

	return result;
}

/**
 * Checks that value is <code>null</code>.
 * @param {Object} value The value to check.
 * @param {String} description Optional description of this assertion.
 * @return {Boolean} The result of assertion.
 */
Aeon.Utils.Assert.isNull = function Assert$isNull(value, description)
{
	var result = (value == null);
	if (result == false)
		Aeon.Utils.Assert.raiseError(description || ("Value should be null."));

	return result;
}

/**
 * Checks that value is not <code>null</code>.
 * @param {Object} value The value to check.
 * @param {String} description Optional description of this assertion.
 * @return {Boolean} The result of assertion.
 */
Aeon.Utils.Assert.isNotNull = function Assert$isNotNull(value, description)
{
	var result = (value != null);
	if (result == false)
		Aeon.Utils.Assert.raiseError(description || ("Value should not be null."));

	return result;
}

/**
 * Checks that value1 and value2 are equal.
 * @param {Object} value1 First value to check.
 * @param {Object} value2 Second value to check.
 * @param {String} description Optional description of this assertion.
 * @return {Boolean} The result of assertion.
 */
Aeon.Utils.Assert.areEqual = function Assert$areEqual(value1, value2, description)
{
	var result = (value1 == value2);
	if (result == false)
		Aeon.Utils.Assert.raiseError(description || ("Value should be equal."), value1, value2);

	return result;
}

/**
 * Checks that value1 and value2 are not equal.
 * @param {Object} value1 First value to check.
 * @param {Object} value2 Second value to check.
 * @param {String} description Optional description of this assertion.
 * @return {Boolean} The result of assertion.
 */
Aeon.Utils.Assert.areNotEqual = function Assert$areNotEqual(value1, value2, description)
{
	var result = (value1 != value2);
	if (result == false)
		Aeon.Utils.Assert.raiseError(description || ("Value should not be equal."), value1, value2);

	return result;
}

/**
 * Checks that value is a string.
 * @param {Object} value A value to check.
 * @param {String} description Optional description of this assertion.
 * @return {Boolean} The result of assertion.
 */
Aeon.Utils.Assert.isString = function Assert$isString(value, description)
{
	var result = (typeof value == 'string');
	if (result == false)
		Aeon.Utils.Assert.raiseError(description || ("Value should be a string."));

	return result;
}

/**
 * Checks that value is a number.
 * @param {Object} value A value to check.
 * @param {String} description Optional description of this assertion.
 * @return {Boolean} The result of assertion.
 */
Aeon.Utils.Assert.isNumber = function Assert$isNumber(value, description)
{
	var result = (typeof value == 'number');
	if (result == false)
		Aeon.Utils.Assert.raiseError(description || ("Value should be a number."));

	return result;
}

/**
 * Checks that value is a object.
 * @param {Object} value A value to check.
 * @param {String} description Optional description of this assertion.
 * @return {Boolean} The result of assertion.
 */
Aeon.Utils.Assert.isObject = function Assert$isObject(value, description)
{
	var result = ((value && typeof value == 'object') || (typeof value == 'function'));
	if (result == false)
		Aeon.Utils.Assert.raiseError(description || ("Value should be an object."));

	return result;
}

/**
 * Asserts that the value is an HTML element or XML node.
 * @param {Object} value The value to check.
 * @param {String} description Optional description of this assertion.
 * @return {Boolean} The result of assertion.
 */
Aeon.Utils.Assert.isNode = function Assert$isNode(value, description)
{
	var result = Type.isNode(value);
	if (result == false)
		Aeon.Utils.Assert.raiseError(description || ("Value should be a node."));

	return result;
}

/**
 * Asserts that the value is an HTML or XML element.
 * @param {Object} value The value to check.
 * @param {String} description Optional description of this assertion.
 * @return {Boolean} The result of assertion.
 */
Aeon.Utils.Assert.isElement = function Assert$isElement(value, description)
{
	var result = Type.isElement(value);
	if (result == false)
	{
		Aeon.Utils.Assert.raiseError(description || ("Value should be an element."));
	}
	return result;
}

/**
 * Asserts that the value is an HTML element.
 * @param {Object} value The value to check.
 * @param {String} description Optional description of this assertion.
 * @return {Boolean} The result of assertion.
 */
Aeon.Utils.Assert.isHtmlElement = function Assert$isHtmlElement(value, description)
{
	var result = Type.isHtmlElement(value);
	if (result == false)
		Aeon.Utils.Assert.raiseError(description || ("Value should be an html element."));

	return result;
}

/**
 * Asserts that the value is an XML document.
 * @param {Object} value The value to check.
 * @param {String} description Optional description of this assertion.
 * @return {Boolean} The result of assertion.
 */
Aeon.Utils.Assert.isDocument = function Assert$isDocument(value, description)
{
	var result = Type.isDocument(value);
	if (result == false)
		Aeon.Utils.Assert.raiseError(description || ("Value should be an xml document."));

	return result;
}

/**
 * Checks that value is a array.
 * @param {Object} value A value to check.
 * @param {String} description Optional description of this assertion.
 * @return {Boolean} The result of assertion.
 */
Aeon.Utils.Assert.isArray = function Assert$isArray(value, description)
{
	var result = ((value && typeof value == 'object') && value.constructor == Array);
	if (result == false)
		Aeon.Utils.Assert.raiseError(description || ("Value should be an array."));

	return result;
}

/**
 * Checks that value is a function.
 * @param {Object} value A value to check.
 * @param {String} description Optional description of this assertion.
 * @return {Boolean} The result of assertion.
 */
Aeon.Utils.Assert.isFunction = function Assert$isFunction(value, description)
{
	var result = (typeof value == 'function');
	if (result == false)
		Aeon.Utils.Assert.raiseError(description || ("Value should be a function."));

	return result;
}

/**
 * Checks that the specified object implements the specified interface.
 * @param {Object} object The object to check.
 * @param {Object} interface The interface to check against.
 * @param {String} description Optional description of this assertion.
 * @return {Boolean} The result of assertion.
 */
Aeon.Utils.Assert.implements = function Assert$implements(object, interface, description)
{
	var result = Type.implements(object, interface);
	if (result == false)
		Aeon.Utils.Assert.raiseError(description || ("Value should implement the specified interface."));

	return result;
}

/**
 * Checks that an object is instance of a class.
 * @param {Object} object An object to check.
 * @param {Object} objectClass The class that the object should be an instance of
 * @param {String} description Optional description of this assertion.
 * @return {Boolean} The result of assertion.
 */
Aeon.Utils.Assert.instanceOf = function Assert$instanceOf(object, objectClass, description)
{
	var result = (object instanceof objectClass);
	if (result == false)
		Aeon.Utils.Assert.raiseError(description || ("Object should be an instance of " + objectClass.getName()), object, objectClass);

	return result;
}

Aeon.Utils.Assert.raiseError = function Assert$raiseError(errorText)
{
	$log.error(errorText);
	throw new Error(errorText);
}

/**
 * Provides utility methods for working with cookies.
 */
Aeon.Utils.Cookie = new function Cookie()
{
}

/**
 * Sets/adds a cookie to the document.cookies collection
 * @param {String} name The name of the cookie. Required.
 * @param {String} value The value of the cookie. Required.
 * @param {String} expires An expression, signifying the expires property of the cookie. See <code>Aeon.Utils.Cookie.dateCalc</code>. Optional.
 * @param {String} domain The domain property of the cookie. Optional.
 * @param {Boolean} secure The secure property of the cookie. Optional.
 * @return {String} The value of the cookie that has just been set.
 */
Aeon.Utils.Cookie.set = function Cookie$set(name, value, expires, domain, path, secure)
{
	var cookie = name + "=" + escape(value);

	if (expires) cookie += "; expires=" + $cookie.dateCalc(expires);
	if (domain)  cookie += "; domain="  + domain;
	if (path)    cookie += "; path="    + path;
	if (secure)  cookie += "; secure=true";

	document.cookie = cookie;

	return $cookie.get(name);
}

/**
 * Returns a cookie with the specified name from the document.cookies collection, if present,
 * and a <code>null</code> if not.
 * @param {String} name The name of the cookie. Required.
 * @return {String|null} The value of the cookie, if found, and a <code>null</code> otherwise.
 */
Aeon.Utils.Cookie.get = function Cookie$get(name)
{
	var cookies = document.cookie.split("; ");
	for (var i = 0; i < cookies.length; i++)
	{
		var cookieName  = cookies[i].substring(0, cookies[i].indexOf("="));
		var cookieValue = cookies[i].substring(cookies[i].indexOf("=") + 1, cookies[i].length);
		if (cookieName == name)
		{
			if (cookieValue.indexOf("&") != -1)
			{
				var pairs  = strValue.split("&");
				var cookie = new Object();
				for (var i in pairs)
				{
					var arrTemp = pairs[i].split("=");
					cookie[arrTemp[0]] = arrTemp[1];
				}
				return cookie;
			}
			else
				return unescape(cookieValue);
		}
	}
	return null;
}

/**
 * Deletes a cookie with the specified name from the document.cookies collection, by setting its expires property
 * to the current date [expire now].
 * @param {String} name The name of the cookie. Required.
 */
Aeon.Utils.Cookie.remove = function Cookie$remove(name)
{
	var dateObj = new Date();
	document.cookie = name + "=null; expires=" + dateObj.toGMTString();
}

Aeon.Utils.Cookie.dateCalc = function Cookie$dateCalc(offsetString)
{
	var dateObj = new Date();
	var multip = new Object();
	var offsetTime;
	multip['s'] = 1000*1;
	multip['m'] = 1000*60;
	multip['h'] = 1000*60*60;
	multip['d'] = 1000*60*60*24;
	multip['M'] = 1000*60*60*24*30;
	multip['y'] = 1000*60*60*24*365;

	if (!offsetString || (offsetString == '') || (offsetString.toLowerCase() == 'now'))
	{ // this will set the time calc to now
		offsetTime = 0;
	}
	else if ( matches = offsetString.match( /^([+-]?(\d+|\d*\.\d*))([mhdMy]?)/ ) )
	{ // perform calculation if offsetString matches specification
		offsetTime = multip[matches[3]] * matches[2];
	}
	else
	{ // otherwise assume that we're providing the date ourselves so just return original
		return offsetString;
	}
	dateObj.setTime(dateObj.getTime() + offsetTime);
	return (dateObj.toGMTString());
}

/**
 * Provides utility methods for working with css.
 */
Aeon.Utils.Css = function Css(element, property, value)
{
	if (!element || !element.tagName)
		return;

	if (arguments.length == 1)
		return element.style.cssText;

	else if (arguments.length == 2)
	{
		return (element.style[property] || $dom.currentStyle(element)[property]);
	}
	else
		return (element.style[property] = value);
}

Aeon.Utils.Css.containsClass = function Css$containsClass(element)
{
	if (!Type.isElement(element) || arguments.length < 2)
		return null;

	var contains = true;
	for (var i = 1; i < arguments.length; i++)
	{
		var rxp = new RegExp("(?:^|\\s)" + arguments[i] + "(?:$|\\s)");
		if (!element.className.match(rxp))
			contains = false;

		if (!contains)
			break;
	}
	return contains;
}

Aeon.Utils.Css.addClass = function Css$addClass(element, className)
{
	if (!Type.isElement(element))
		return;

	if (!$css.containsClass(element, className))
		element.className += " " + className;
}

Aeon.Utils.Css.removeClass = function Css$removeClass(element, className)
{
	if (!Type.isElement(element))
		return;

	var rxp = new RegExp("(^|\\s)" + className + "($|\\s)", "g");
	element.className = element.className.replace(rxp, "$1$2");
	if (element.className.trim() == String.EMPTY)
	{
		element.removeAttribute("class");
	}
}

Aeon.Utils.Css.replaceClass = function Css$replaceClass(element, oldClass, newClass)
{
	$css.removeClass(element, oldClass);
	$css.addClass(element, newClass);
}

Aeon.Utils.Css.extractCss = function Css$extractCss(selectorText)
{
	for (var i = 0; i < document.styleSheets.length; i++)
	{
		var rules = document.styleSheets[i].rules || document.styleSheets[i].cssRules;
		for (var j = 0; j < rules.length; j++)
			if (rules[j].selectorText && rules[j].selectorText.match(selectorText))
				return rules[j].style.cssText;
	}
	return String.EMPTY;
}

Aeon.Utils.Css.display = function Css$display(elem)
{
	var elems = Array.fromArguments(arguments);
	for (var i = 0; i < elems.length; i++)
	{
		var elem = elems[i];
		if (elem == null || elem.style == null)
			continue;

		var displayValue;
		if ($dom.is_ie)
			displayValue = elem.tagName == "SPAN" ? "inline" : "block";
		else
		{
			var tagName = elem.tagName.toLowerCase();
			if (tagName.match(/^(a|b|code|em|i|img|input|label|span|textarea)$/))
				displayValue = "inline";
			else if (tagName == "tr")
				displayValue = "table-row";
			else if (tagName == "td")
				displayValue = "table-cell";
			else if (tagName == "table")
				displayValue = "table";
			else
				displayValue = "block";
		}
		$css(elem, "display", displayValue);
	}
}

Aeon.Utils.Css.undisplay = function Css$undisplay(element)
{
	var elems = Array.fromArguments(arguments);
	for (var i = 0; i < elems.length; i++)
	{
		var elem = elems[i];
		if (elem == null || elem.style == null)
			continue;

		elem.style.display = "none";
	}
}

Aeon.Utils.Css.alpha = function Css$alpha(element, value)
{
	if (element == null || element.style == null)
		return;

	var setValue = isNaN(parseInt(value)) ? null : parseInt(value);
	var resultValue = 100;
	if ($dom.is_ie)
	{
		var filter = null;
		for (var i = 0; i < element.filters.length; i++)
		{
			if (element.filters[i].opacity != null)
			{
				filter = element.filters[i];
				if (setValue != null)
					filter.opacity = setValue;

				resultValue = filter.opacity;
			}
		}
		if (filter == null && setValue)
		{
			element.style.filter = "alpha(opacity={0})".format(setValue);
			if (element.filters && element.filters[0])
				resultValue = element.filters[0].opacity;
		}
	}
	else
	{
		if (setValue != null)
			$css(element, "opacity", setValue / 100);

		var opacity = $css(element, "opacity");
		if (opacity)
			resultValue *= opacity;
	}

	return resultValue;
}

Aeon.Utils.Css.show = function Css$show(element, inherit)
{
	if (element == null || element.style == null)
		return;

	element.style.visibility = inherit == true ? "inherit" : "visible";
}

Aeon.Utils.Css.hide = function Css$hide(element)
{
	if (element == null || element.style == null)
		return;

	element.style.visibility = "hidden";
}

Aeon.Utils.Css.pixelLeft = function Css$pixelLeft(elem, value)
{
	return $css.pixelProperty(elem, "left", value);
}

Aeon.Utils.Css.pixelRight = function Css$pixelRight(elem, value)
{
	return $css.pixelProperty(elem, "right", value);
}

Aeon.Utils.Css.pixelTop = function Css$pixelTop(elem, value)
{
	return $css.pixelProperty(elem, "top", value);
}

Aeon.Utils.Css.pixelBottom = function Css$pixelBottom(elem, value)
{
	return $css.pixelProperty(elem, "bottom", value);
}

Aeon.Utils.Css.pixelWidth = function Css$pixelWidth(elem, value)
{
	return $css.pixelProperty(elem, "width", value);
}

Aeon.Utils.Css.pixelHeight = function Css$pixelHeight(elem, value)
{
	return $css.pixelProperty(elem, "height", value);
}

Aeon.Utils.Css.pixelProperty = function Css$pixelProperty(elem, prop, value)
{
	if (elem == null || elem.style == null)
		return null;

	if (!isNaN(parseInt(value)))
		$css(elem, prop, value + "px");

	var current = $css(elem, prop);
	if (current == "auto")
		return 0;
	else
		return parseInt(current);
}

Aeon.Utils.Css.pixelCoord = function Css$pixelCoord(elem, left, top, right, bottom)
{
	if (elem == null || elem.style == null)
		return null;

	if (!isNaN(parseInt(arguments[1])))
		$css(elem, "left", arguments[1] + "px");
	if (!isNaN(parseInt(arguments[2])))
		$css(elem, "top", arguments[2] + "px");
	if (!isNaN(parseInt(arguments[3])))
		$css(elem, "right", arguments[3] + "px");
	if (!isNaN(parseInt(arguments[4])))
		$css(elem, "bottom", arguments[4] + "px");

	var coord = {
		left:   parseInt($css(elem, "left"))   || null,
		top:	parseInt($css(elem, "top"))	|| null,
		right:  parseInt($css(elem, "right"))  || null,
		bottom: parseInt($css(elem, "bottom")) || null
	};

	return coord;
}

/**
 * Provides utility methods for working with dom.
 */
Aeon.Utils.Dom = new function Dom()
{
	var agt = navigator.userAgent.toLowerCase();

	/**
	 * @type Boolean
	 */
	this.is_moz = (agt.indexOf("firefox/") != -1);
	/**
	 * @type Boolean
	 */
	this.is_opera  = (agt.indexOf("opera/") != -1);
	/**
	 * @type Boolean
	 */
	this.is_safari = (agt.indexOf('applewebkit/') != -1);
	/**
	 * @type Boolean
	 */
	this.is_chrome = (agt.indexOf('chrome/') != -1);
	/**
	 * @type Boolean
	 */
	this.is_ie  = !this.is_opera && agt.indexOf("msie") != -1;
	/**
	 * @type Boolean
	 */
	this.is_ie7 = this.is_ie && agt.indexOf("msie 7") != -1;
	/**
	 * @type Boolean
	 */
	this.is_ie6 = this.is_ie && agt.indexOf("msie 6") != -1;

	if (!window.XMLHttpRequest)
	{
		window.XMLHttpRequest = function window$XMLHttpRequest() /**/
		{
			var progIDs = ['Msxml2.XMLHTTP.3.0', 'Msxml2.XMLHTTP'];
			for (var i = 0, l = progIDs.length; i < l; i++)
			{
				try
				{
					return new ActiveXObject(progIDs[i]);
				}
				catch (ex) {}
			}
			return null;
		}
	}
}

Aeon.Utils.Dom.offsetLeft = function Dom$offsetLeft(elem)
{
	var node = elem;
	var left = 0;

	while (node && node.nodeName)
	{
		left += node.offsetLeft - node.scrollLeft;
		node = node.offsetParent;
	}
	return left;
}

Aeon.Utils.Dom.offsetTop = function Dom$offsetTop(elem)
{
	var node = elem;
	var top = 0;

	while (node && node.nodeName)
	{
		top += node.offsetTop - node.scrollTop;
		node = node.offsetParent;
	}
	return top;
}

Aeon.Utils.Dom.parent = function Dom$parent(elem, expression)
{
	if (elem == null)
		return null;

	return Aeon.Utils.Dom.parentOrSelf(elem.parentNode, expression);
}

Aeon.Utils.Dom.parentOrSelf = function Dom$parentOrSelf(elem, expression)
{
	while (elem)
	{
		if (Sizzle.matches(expression, [elem]).length == 1)
			break;

		elem = elem.parentNode;
	}

	return elem;
}

Aeon.Utils.Dom.innerText = function Dom$innerText(node, value)
{
	$assert.isNode(node);

	if (arguments.length > 1 && node.textContent != null)
		node.textContent = value;

	else if (arguments.length > 1 && node.text != null)
		node.text = value;

	else if (arguments.length > 1 && node.innerText != null)
		node.innerText = value;

	return (
		node.textContent ||
		node.text ||
		node.innerText
	);
}

Aeon.Utils.Dom.currentStyle = function Dom$currentStyle(element)
{
	if (element == null)
		return null;

	if ($dom.is_ie)
		return element.currentStyle;
	else if (window.getComputedStyle)
		return getComputedStyle(element, null);
	else
		return element.style;
}

Aeon.Utils.Dom.uniqueID = function Dom$uniqueID(element)
{
	if (element == null)
		return null;

	if (element.uniqueID)
		return element.uniqueID;

	if (Aeon.Utils.Dom.__uid == null)
		Aeon.Utils.Dom.__uid = {};

	var uniqueID = new Date().getTime();
	while (Aeon.Utils.Dom.__uid[uniqueID] != null)
		uniqueID++;

	Aeon.Utils.Dom.__uid[uniqueID] = uniqueID;

	element.uniqueID = "__unique" + uniqueID;
	return element.uniqueID;
}

Aeon.Utils.Dom.elementContains = function Dom$elementContains(parent, child)
{
	if (parent == null || child == null)
		return false;
	else
	{
		if ($dom.is_ie)
			return parent.contains(child);
		else
			return (parent == child ||
				$dom.elementContains(parent, child.parentNode));
	}
}

Aeon.Utils.Dom.makeUnselectable = function Dom$makeUnselectable(element, recursive)
{
	if (!Type.isHtmlElement(element))
		return;

	if ($dom.is_ie)
		element.setAttribute("unselectable", "on");
	else
	{
		element.style.userSelect =
		element.style.MozUserSelect = "none";
	}

	if (recursive == true)
	{
		var childNodes = element.getElementsByTagName("*");
		for (var i = 0; i < childNodes.length; i++)
		{
			if ($dom.is_ie)
				childNodes[i].setAttribute("unselectable", "on");
			else
			{
				childNodes[i].style.userSelect =
				childNodes[i].style.MozUserSelect = "none";
			}
		}
	}
}

Aeon.Utils.Dom.makeSelectable = function Dom$makeSelectable(element, recursive)
{
	if (!Type.isHtmlElement(element))
		return;

	if ($dom.is_ie)
		element.removeAttribute("unselectable");
	else
	{
		element.style.userSelect =
		element.style.MozUserSelect = String.EMPTY;
	}

	if (recursive == true)
	{
		var childNodes = element.getElementsByTagName("*");
		for (var i = 0; i < childNodes.length; i++)
		{
			if ($dom.is_ie)
				childNodes[i].removeAttribute("unselectable");
			else
			{
				childNodes[i].style.userSelect =
				childNodes[i].style.MozUserSelect = String.EMPTY;
			}
		}
	}
}

/**
 * Provides utility methods for working with events.
 */
Aeon.Utils.Evt = function Evt(e)
{
	if (e != null)
		return e;

	return window.event;
}

Aeon.Utils.Evt.srcElement = function Evt$srcElement(e)
{
	var e = $evt(e);
	if (e == null)
		return null;

	if ($dom.is_ie)
		return e.srcElement;
	else
	{
		if (e.target.nodeType == 1)
			return e.target;
		else
			return e.target.parentNode;
	}
}

/**
 * Adds an event listener to the supplied target.
 * The target should be a DOM object, and the event one of the events supported by it.
 * @param {Object} target The DOM element to add the event listener to.
 * @param {String} eventName The name of the event to listen to.
 * @param {Function} eventHandler The event listener.
 * @param {Boolean} insertBefore If true, the listener will be added at the beginning of
 * the array of listeners.
 */
Aeon.Utils.Evt.addListener = function Evt$addListener(target, eventName, eventHandler, insertBefore)
{
	if (target == null)
		return;

	var elems = Array.fromArguments(arguments, 0, 0);
	for (var i = 0; i < elems.length; i++)
	{
		target = elems[i];

		if (target.$listeners == null)
			target.$listeners = {};

		if (target == window && eventName == "onready")
		{
			var enabled = $evt.setupOnReady();
			if (!enabled)
				eventName = "onload";
		}

		if (eventName.indexOf("on") != 0)
			eventName = "on" + eventName;

		if (target.$listeners[eventName] == null)
		{
			target.$listeners[eventName] = new Array;
			if (target[eventName] != null)
				target.$listeners[eventName].push(target[eventName]);
		}

		var handlerExists = false;
		for (var j = 0; j < target.$listeners[eventName].length; j++)
			if (target.$listeners[eventName][j] == eventHandler)
				handlerExists = true;

		// only assign an event handler once
		if (handlerExists == false)
		{
			if (insertBefore == true)
				target.$listeners[eventName].unshift(eventHandler);
			else
				target.$listeners[eventName].push(eventHandler);
		}

		target[eventName] = $evt.onEvent;
	}
}

/**
 * Sets up crossbrowser (FF & IE only) support for 'window.onready' event.
 * @return {Boolean} True if 'window.onready' can be used in the current browser.
 */
Aeon.Utils.Evt.setupOnReady = function Evt$setupOnReady()
{
	var result = false;
	if (window.is_moz)
	{
		document.addEventListener("DOMContentLoaded", $evt.onReady, false);
		result = true;
	}
	/*@cc_on @*/
	/*@if (@_win32)
	document.write("<s" + "cript id=__aeon_ie_onload defer src=javascript:void(0)><\/s" + "cript>");
	var script = document.getElementById("__aeon_ie_onload");
	script.onreadystatechange = function script$onreadystatechange()
	{
		if (this.readyState == "complete")
			$evt.onReady();
	};
	result = true;
	/*@end @*/
	return result;
}

/**
 * Callback for window.onready
 */
Aeon.Utils.Evt.onReady = function Evt$onReady()
{
	$evt.fireEvent(window, null, "ready");
}

/**
 * Callback for standard dom events
 * @param {Object} e The standard DOMEvent instance (non IE)
 */
Aeon.Utils.Evt.onEvent = function Evt$onEvent(e)
{
	var e = $evt(e);
	if (e)
		return $evt.fireEvent(this, e);
}

/**
 * Handles the firing of events added with <code>$evt.addListener</code>.
 * @param {Object} target The DOM element that fired the event.
 * @param {Object} eventHandler The standard DOMEvent instance
 * @param {String} eventName The name of the event that was fired.
 */
Aeon.Utils.Evt.fireEvent = function Evt$fireEvent(target, eventObj, eventName)
{
	if (eventObj != null)
		eventName = eventObj.type;

	if (target != null && target.$listeners != null && eventName != null)
	{
		if (eventName.indexOf("on") != 0)
			eventName = "on" + eventName;

		var finalResult = null;
		var currentResult = null;

		for (var i = 0; i < target.$listeners[eventName].length; i++)
		{
			currentResult = target.$listeners[eventName][i].call(target, eventObj);
			if (currentResult != undefined)
				finalResult = currentResult;
		}

		return finalResult;
	}
}

Aeon.Utils.Evt.removeListener = function Evt$removeListener(targetElem, eventName, eventHandler)
{
	if (!targetElem || !targetElem.nodeName)
		return;

	if (eventName.indexOf("on") != 0)
		eventName = "on" + eventName;

	if (targetElem.$listeners != null && targetElem.$listeners[eventName] != null)
	{
		for (var i = 0; i < targetElem.$listeners[eventName].length; i++)
			if (targetElem.$listeners[eventName][i] == eventHandler)
				targetElem.$listeners[eventName].splice(i, 1);
	}
}

Aeon.Utils.Evt.preventDefault = function Evt$preventDefault(e)
{
	var e = $evt(e);
	if (!$dom.is_ie)
		e.preventDefault();

	return false;
}

Aeon.Utils.Evt.cancel = function Evt$cancel(e)
{
	var e = $evt(e);
	if ($dom.is_ie)
		e.cancelBubble = true;
	else
		e.stopPropagation();

	e.returnValue = false;
	return false;
}

/**
 * Provides logging methods.
 */
Aeon.Utils.Log = new function Log()
{
	if (window.console == null)
		window.console = new Object;

	var emptyFx = new Function;

	window.console.log = Type.isFunction(window.console.log) ? window.console.log : emptyFx;
	window.console.group = Type.isFunction(window.console.group) ? window.console.group : emptyFx;
	window.console.groupEnd = Type.isFunction(window.console.groupEnd) ? window.console.groupEnd : emptyFx;
	window.console.info = Type.isFunction(window.console.info) ? window.console.info : window.console.log;
	window.console.warn = Type.isFunction(window.console.warn) ? window.console.warn : window.console.log;
	window.console.error = Type.isFunction(window.console.error) ? window.console.error : window.console.log;
	window.console.dir = Type.isFunction(window.console.dir) ? window.console.dir : window.console.log;
}

Aeon.Utils.Log.Level = {};
Aeon.Utils.Log.Level.ALL = 0;
Aeon.Utils.Log.Level.INFO = 1;
Aeon.Utils.Log.Level.WARN = 2;
Aeon.Utils.Log.Level.ERROR = 4;
Aeon.Utils.Log.Level.DEBUG = 5;
Aeon.Utils.Log.Level.NONE = 9;

Aeon.Utils.Log.level = Aeon.Utils.Log.Level.INFO;

Aeon.Utils.Log.group = function Log$group(message, level)
{
	if ($log.level > level)
		return;

	console.group(message);
}

Aeon.Utils.Log.groupEnd = function Log$groupEnd(level)
{
	if ($log.level > level)
		return;

	console.groupEnd();
}

Aeon.Utils.Log.message = function Log$message(message)
{
	var text = String.format.apply(this, arguments);

	console.log(text);
}

Aeon.Utils.Log.info = function Log$info()
{
	if ($log.level > $log.Level.INFO)
		return;

	var text = String.format.apply(this, arguments);
	console.info(text);
}

Aeon.Utils.Log.warn = function Log$warn()
{
	if ($log.level > $log.Level.WARN)
		return;

	var text = String.format.apply(this, arguments);
	console.warn(text);
}

Aeon.Utils.Log.error = function Log$error()
{
	if ($log.level > $log.Level.ERROR)
		return;

	var caller = arguments.callee.caller;
	var callee = arguments.callee;
	var stackTrace = callee.getStackTrace(caller).toString();

	var text = String.format.apply(this, arguments);
	console.error(text);
	console.log(stackTrace);
}

Aeon.Utils.Log.debug = function Log$debug()
{
	if ($log.level > $log.Level.DEBUG)
		return;

	var text = String.format.apply(this, arguments);
	console.info(text);
}

Aeon.Utils.Log.dir = function Log$dir()
{
	var text = String.format.apply(this, arguments);
	console.dir(text);
}

Aeon.Utils.Log.trace = function Log$trace()
{
	var text = String.format.apply(this, arguments);

	var caller = arguments.callee.caller;
	var callee = arguments.callee;
	var stackTrace = callee.getStackTrace(caller).toString();

	console.group(text);
	console.log(stackTrace);
	console.groupEnd();
}

Aeon.Utils.Xml = function Xml(document)
{
	if (Type.isDocument(document))
		return $xml.serializeToString(document);
}

Aeon.Utils.Xml.PROGID_DOCUMENT = "MSXML2.DomDocument";
Aeon.Utils.Xml.PROGID_DOCUMENT_FT = "MSXML2.FreeThreadedDomDocument";
Aeon.Utils.Xml.PROGID_XSLTEMPLATE = "MSXML2.XslTemplate";

Aeon.Utils.Xml.outerXml = function Xml$outerXml(xmlNode)
{
	return $xml.serializeToString(xmlNode);
}

Aeon.Utils.Xml.innerText = function Xml$innerText(node, xpath, namespaces)
{
	$assert.isNode(node);

	var childNode = $xml.selectSingleNode(node, xpath, namespaces);
	if (childNode)
		return $dom.innerText(childNode);
}

Aeon.Utils.Xml.serializeToString = function Xml$serializeToString(xmlNode)
{
	$assert.isNode(xmlNode);

	if (window.ActiveXObject)
		return xmlNode.xml;
	else
		return (new XMLSerializer()).serializeToString(xmlNode);
}

Aeon.Utils.Xml.serializeToObject = function Xml$serializeToObject(xmlNode)
{
	if (xmlNode == null || xmlNode.nodeType == null)
		return null;

	var result = {};
	if (xmlNode.nodeType == 9)
	{
		var rootName = xmlNode.documentElement.nodeName;
		result[rootName] = $xml.serializeToObject(xmlNode.documentElement);
	}
	else if (xmlNode.nodeType == 1)
	{
		for (var i = 0; i < xmlNode.attributes.length; i++)
		{
			result[xmlNode.attributes[i].name] =
				xmlNode.getAttribute(xmlNode.attributes[i].name);
		}
		for (var i = 0; i < xmlNode.childNodes.length; i++)
		{
			var child = xmlNode.childNodes[i];
			if (child.nodeType == 1)
			{
				var parsed = $xml.serializeToObject(child);
				if (result[child.nodeName] != null)
				{
					if (!Type.isArray(result[child.nodeName]))
						result[child.nodeName] = [result[child.nodeName]];

					result[child.nodeName].push(parsed);
				}
				else
					result[child.nodeName] = parsed;
			}
			if (child.nodeType == 3 && child.nodeValue.trim() != "")
				result.$text = child.nodeValue;
		}
	}
	return result;
}

Aeon.Utils.Xml.createNsResolver = function Xml$createNsResolver(namespaces)
{
	$assert.isObject(namespaces);

	return function NsResolver(prefix) /**/
	{
		return namespaces[prefix];
	};
}

Aeon.Utils.Xml.selectSingleNode = function Xml$selectSingleNode(parent, xPath, namespaces)
{
	$assert.isNode(parent);
	$assert.isString(xPath);

	var doc = parent.nodeType == 9
		? parent
		: parent.ownerDocument;

	if (doc.evaluate)
	{
		var nsResolver = namespaces
			? $xml.createNsResolver(namespaces)
			: null;

		var xPathResult = doc.evaluate(xPath, parent, nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
		return xPathResult.singleNodeValue;
	}
	else
	{
		var nslist = new Array;
		for (var prefix in namespaces)
			nslist.push('xmlns:{0}="{1}"'.format(prefix, namespaces[prefix]));

		doc.setProperty("SelectionNamespaces", nslist.join(" "));
		return parent.selectSingleNode(xPath);
	}
}

Aeon.Utils.Xml.selectNodes = function Xml$selectNodes(parent, xPath, namespaces)
{
	$assert.isNode(parent);
	$assert.isString(xPath);

	var doc = parent.nodeType == 9
		? parent
		: parent.ownerDocument;

	if (doc.evaluate)
	{
		var nsResolver = namespaces
			? $xml.createNsResolver(namespaces)
			: null;

		var xPathResult = doc.evaluate(xPath, parent, nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);

		var node = xPathResult.iterateNext();
		var nodeList = new Array;
		while (node)
		{
			nodeList.push(node);
			node = xPathResult.iterateNext();
		}
		return nodeList;
	}
	else
	{
		var nslist = new Array;
		for (var prefix in namespaces)
			nslist.push('xmlns:{0}="{1}"'.format(prefix, namespaces[prefix]));

		doc.setProperty("SelectionNamespaces", nslist.join(" "));
		return parent.selectNodes(xPath);
	}
}

Aeon.Utils.Xml.loadFile = function Xml$loadFile(url, oncomplete, onerror)
{
	$assert.isString(url);
	$assert.isFunction(oncomplete);

	var request = new Aeon.WebRequest(url);
	request.addListener("oncomplete", oncomplete);
	if (Type.isFunction(onerror))
		request.addListener("onerror", onerror);

	request.execute();
	return request;
}

Aeon.Utils.Xml.loadXmlDocument = function Xml$loadXmlDocument(url, onload, onerror)
{
	$assert.isString(url);
	$assert.isFunction(onload);

	function Xml$openXmlDocument$loaded(event) /**/
	{
		var request = event.source;
		var document = $xml.createXmlDocument(request.responseText);
		onload(document);
	}

	$xml.loadFile(url, Xml$openXmlDocument$loaded, onerror);
}

Aeon.Utils.Xml.createXmlDocument = function Xml$createXmlDocument(xmlText, freeThreaded)
{
	var xmlDocument;

	if (window.ActiveXObject)
	{
		if (freeThreaded)
			xmlDocument = new ActiveXObject(Aeon.Utils.Xml.PROGID_DOCUMENT_FT);
		else
			xmlDocument = new ActiveXObject(Aeon.Utils.Xml.PROGID_DOCUMENT);

		if (xmlText)
		{
			xmlDocument.loadXML(xmlText);
			if (xmlDocument.parseError != 0)
			{
				$log.error("Error loading xml text: {0}{1}.".format(
					xmlDocument.parseError.reason,
					xmlDocument.parseError.line
						? " (on line " + xmlDocument.parseError.line + ")"
						: String.EMPTY
				));
			}
		}
	}
	else
	{
		if (xmlText)
		{
			var parser = new DOMParser();
			xmlDocument = parser.parseFromString(xmlText, "text/xml");
			if (xmlDocument.documentElement.localName == "parsererror")
			{
				$log.error($dom.innerText(xmlDocument.documentElement));
			}
		}
		else
		{
			xmlDocument = document.implementation.createDocument(String.EMPTY, String.EMPTY, null);
		}
	}

	return xmlDocument;
}

Aeon.Utils.Xml.createXslProcessor = function Xml$createXslProcessor(xmlDocument)
{
	$assert.isDocument(xmlDocument);

	var processor = null;
	if (window.ActiveXObject)
	{
		var ftDocument = $xml.createXmlDocument(xmlDocument.xml, true);
		var template = new ActiveXObject($xml.PROGID_XSLTEMPLATE);
		template.stylesheet = ftDocument;
		processor = template.createProcessor();
	}
	else
	{
		processor = new XSLTProcessor();
		processor.importStylesheet(xmlDocument);
	}

	return processor;
}


Aeon.Utils.Xml.transformToDocument = function Xml$transformToDocument(xmlDocument, xslProcessor)
{
	$assert.isDocument(xmlDocument);
	$assert.isNotNull(xslProcessor);

	var result = null;
	if (window.ActiveXObject)
	{
		result = $xml.createXmlDocument();
		xslProcessor.input = xmlDocument;
		xslProcessor.output = result;
		xslProcessor.transform();
	}
	else
	{
		result = xslProcessor.transformToDocument(xmlDocument);
	}

	return result;
}

Aeon.Utils.Xml.transformToString = function Xml$transformToString(xmlDocument, xslProcessor)
{
	var resultDoc = $xml.transformToDocument(xmlDocument, xslProcessor);
	return $xml.serializeToString(resultDoc);
}

Aeon.Utils.Xml.NsResolver = function NsResolver(prefix)
{
	if (this.$ns == null)
		this.$ns = {};

	return this.$ns[prefix];
}

Aeon.Utils.Xml.NsResolver.prototype.addNamespace = function (prefix, namespace)
{
	$assert.isString(prefix);
	$assert.isString(namespace);

	if (this.$ns == null)
		this.$ns = {};

	this.$ns[prefix] = namespace;
}


Aeon.QueryString = function QueryString(url)
{
	this.scriptName = String.EMPTY;
	this.scriptPath = String.EMPTY;
	this.scriptNameFull = String.EMPTY;
	this.serverNameFull = String.EMPTY;
	this.protocol = String.EMPTY;
	this.serverName = String.EMPTY;
	this.query = String.EMPTY;
	this.hash = String.EMPTY;

	this.param = {};

	if (url != null)
		this.parse(url);
	else
		this.parse(location.href);
}

Aeon.QueryString.prototype.getItem = function QueryString$getItem(itemKey)
{
	return this.param[itemKey];
}

Aeon.QueryString.prototype.parse = function QueryString$parse(url)
{
	if (url == null)
		return;

	this.url = new String(url);
	if (this.url.match(/^(((https?|ftp):\/\/([a-z-A-Z0-9.]+))([^#\?]+))?(?:\?([^#]*))?(?:\#(.*))?$/))
	{
		this.scriptNameFull = RegExp.$1;
		this.scriptPath = RegExp.$5;
		this.serverName = RegExp.$4;
		this.serverNameFull = RegExp.$2;
		this.protocol = RegExp.$3;
		this.query = RegExp.$6;
		this.hash = RegExp.$7;

		if (!scriptPath.match(/\//))
		{
			scriptName = scriptPath;
			scriptPath = String.EMPTY;
		}
		else if (scriptPath.match(/^(.*\/)(.*)$/))
		{
			scriptPath = RegExp.$1;
			scriptName = RegExp.$2;
		}
	}
	else
	{
		this.query = this.url;
	}

	var param = this.query.split("&");
	for (var i = 0; i < param.length; i++)
	{
		if (param[i].length == 0)
			continue;

		var pair = param[i].split("=");
		var itemKey = pair[0];
		var itemValue = pair[1];

		if (this.param[itemKey] != null)
		{
			if (!Type.isArray(this.param[itemKey]))
				this.param[itemKey] = [this.param[itemKey]];

			this.param[itemKey].push(itemValue);
		}
		else
			this.param[itemKey] = itemValue;
	}
}
Aeon.QueryString.prototype.toString = function QueryString$toString(full)
{
	var param = new Array();
	for (var name in this.param)
	{
		if (this.param[name] && this.param[name].constructor == Array)
		{
			for (var i = 0; i < this.param[name].length; i++)
				param.push(escape(name) + "=" + escape(this.param[name][i]));
		}
		else
		{
			var itemValue = String(this.param[name]).match(/\{.*\}/) ? this.param[name] : escape(this.param[name]);
			param.push(escape(name) + "=" + itemValue);
		}
	}

	var query = param.join("&");
	if (full == true)
		var result = (param.length ? this.scriptNameFull + "?" + query : this.scriptNameFull) + this.hash;
	else
		var result = query;

	return result;
}

/**
 * Represents the event that was fired.
 * @param {Object} eventSource The element that is the source of the event.
 * @param {String} eventType The type/name of the event.
 * @param {Object} eventData Additional data to pass within the event's .data property.
 */
Aeon.Event = function Event(eventSource, eventType, eventData)
{
	this.source = eventSource;
	this.type = eventType;
	this.name = eventType;
	this.data = new Object;
	this.cancel = false;

	for (var prop in eventData)
		this.data[prop] = eventData[prop];
}

/**
 * Dispatches custom events to event listeners.
 * All arguments to the constructor will be interpreted as names of event to register.
 * @constructor
 */
Aeon.Dispatcher = function Dispatcher()
{
	/**
	 * Object's defined event types
	 * @type {Array}
	 */
	this.$events = new Array;

	/**
	 * Object listeners, by event type
	 * @type {Object}
	 */
	this.$listeners = new Object;

	var events = Array.fromArguments(arguments);
	for (var i = 0; i < events.length; i++)
		this.addEvent(events[i]);
}

/**
 * Registers an event with the specified name.
 * @param {String} eventName The name of the event.
 */
Aeon.Dispatcher.prototype.addEvent = function Dispatcher$addEvent(eventName)
{
	if (!this.hasEvent(eventName))
	{
		this.$events.push(eventName);
		this.$listeners[eventName] = new Array;
	}
}

/**
 * Returns true if the specified event already exists.
 * @param {String} eventName The name of the event.
 * @return {Boolean} True if the specified event type exists.
 */
Aeon.Dispatcher.prototype.hasEvent = function Dispatcher$hasEvent(eventName)
{
	return this.$events.contains(eventName);
}

/**
 * Adds an event listener for the specified event type.
 * @param {String} eventName The name of the event.
 * @param {Function} eventListener The event listener.
 */
Aeon.Dispatcher.prototype.addListener = function Dispatcher$addListener(eventName, eventListener)
{
	if (this.hasEvent(eventName))
	{
		if (!this.hasListener(eventListener))
			this.$listeners[eventName].push(eventListener);
	}
	else
	{
		$log.error("Did not add event '{0}' because the current object only supports '{1}'",
			eventName, this.$events.join(", "));
	}
}

/**
 * Removes the specified event listener.
 * @param {String} eventName The name of the event.
 * @param {Function} eventListener The event listener.
 */
Aeon.Dispatcher.prototype.removeListener = function Dispatcher$removeListener(eventName, eventListener)
{
	if (this.hasEvent(eventName))
	{
		for (var i = 0; i < this.$listeners[eventName].length; i++)
		{
			if (this.$listeners[eventName][i] == eventListener)
			{
				this.$listeners[eventName].splice(i, 1);
				return;
			}
		}
	}
}

/**
 * Returns true if the supplied eventType event listener exists.
 * @param {String} eventName The name of the event.
 * @param {Function} eventListener The event listener.
 * @return {Boolean} True if the specified listener exists.
 */
Aeon.Dispatcher.prototype.hasListener = function Dispatcher$hasListener(eventName, eventListener)
{
	if (this.hasEvent(eventName))
	{
		for (var i = 0; i < this.$listeners[eventName].length; i++)
		{
			if (this.$listeners[eventName][i] == eventListener)
				return true;
		}
	}
	return false;
}

/**
 * Fires the specified event.
 * @param {Object} event The event or the name of the event to fire.
 * @param {Object} eventData The event data.
 */
Aeon.Dispatcher.prototype.fireEvent = function Dispatcher$fireEvent(event, eventData)
{
	var eventObj = null;
	var eventType = null;

	if (event instanceof Aeon.Event)
	{
		eventType = event.type;
		eventObj = event;
	}
	else
	{
		if (eventData == null)
			eventData = this.createEventData();

		eventType = event;
		eventObj = new Aeon.Event(this, eventType, eventData);
	}

	var listeners = this.$listeners[eventType];
	if (listeners != null && listeners.length != 0)
	{
		for (var i = 0; i < listeners.length; i++)
		{
			if (Type.isFunction(listeners[i]))
			{
				listeners[i](eventObj);
				if (eventObj.cancel)
					break;
			}
		}

		return eventObj;
	}
	return null;
}

/**
 * Create an object with information about the raised event.
 * This is the default method, it returns an empty object. Override this method in child
 * dispatcher to add actual information.
 * @return {Object} Object with information about the raised event.
 */
Aeon.Dispatcher.prototype.createEventData = function Dispatcher$createEventData()
{
	return {};
}

Aeon.Dispatcher.prototype.toString = function Dispatcher$toString()
{
	var output = [];
	for (var eventType in this.$listeners)
	{
		output.push("{0}: {1} listeners".format(eventType, this.$listeners[eventType].length));
	}
	return "Dispatcher: {{0}}".format(output.join(", "));
}

Aeon.Drag = new Aeon.Dispatcher("ondragstart", "ondragstop", "ondragmove");
Aeon.Drag.active = false;
Aeon.Drag.overlayCss = "position: absolute; background: #fff; opacity: .01; z-index:10000;";

Aeon.Drag.start = function Drag$start(e, target, moveX, moveY)
{
	var event = $evt(e);
	if (event == null || !Type.isElement(target))
		return;

	$drag.moveX = moveX;
	$drag.moveY = moveY;

	$drag.clientX = event.clientX;
	$drag.clientY = event.clientY;

	var pixelTop = parseInt($css.pixelTop(target));
	var pixelLeft = parseInt($css.pixelLeft(target));

	var offsetTop = $dom.offsetTop(target);
	var offsetLeft = $dom.offsetLeft(target);

	$drag.startX = isNaN(pixelLeft) ? offsetLeft : pixelLeft;
	$drag.startY = isNaN(pixelTop) ? offsetTop : pixelTop;

	$drag.targetX = $drag.startX;
	$drag.targetY = $drag.startY;

	$drag.diffX = 0;
	$drag.diffY = 0;

	$drag.target = target;

	if (target.setCapture)
	{
		$drag.target.$onmouseup = $drag.target.onmouseup;
		$drag.target.$onmousemove = $drag.target.onmousemove;

		$drag.target.onmouseup = $drag.stop;
		$drag.target.onmousemove = $drag.move;

		$drag.target.setCapture();
	}
	else
	{
		document.addEventListener("mouseup", $drag.stop, true);
		document.addEventListener("mousemove", $drag.move, true);

		$drag.overlayFrames();
	}
	$drag.active = true;
	$drag.fireEvent("ondragstart");

	return $evt.cancel(e);
}

Aeon.Drag.stop = function Drag$stop(e)
{
	$drag.active = false;
	$drag.fireEvent("ondragstop");

	$drag.clientX = 0;
	$drag.clientY = 0;

	$drag.startX = 0;
	$drag.startY = 0;

	$drag.diffX = 0;
	$drag.diffY = 0;

	$drag.targetX = 0;
	$drag.targetY = 0;

	if ($drag.target.releaseCapture)
	{
		$drag.target.onmouseup = $drag.target.$onmouseup;
		$drag.target.onmousemove = $drag.target.$onmousemove;

		$drag.target.releaseCapture();
	}
	else
	{
		document.removeEventListener("mouseup", $drag.stop, true);
		document.removeEventListener("mousemove", $drag.move, true);

		$drag.removeOverlays();
	}

	$drag.target = null;
	return $evt.cancel(e);
}

Aeon.Drag.move = function Drag$move(e)
{
	$drag.eventX = $evt(e).clientX;
	$drag.eventY = $evt(e).clientY;

	$drag.diffX = $drag.eventX - $drag.clientX;
	$drag.diffY = $drag.eventY - $drag.clientY;

	$drag.targetX = $drag.startX + $drag.diffX;
	$drag.targetY = $drag.startY + $drag.diffY;

	var onDragMove = new Aeon.Event($drag, "ondragmove");

	if ($drag.moveX)
		onDragMove.data.targetX = $drag.targetX;

	if ($drag.moveY)
		onDragMove.data.targetY = $drag.targetY;

	$drag.fireEvent(onDragMove);

	if ($drag.moveX)
		$css.pixelLeft($drag.target, onDragMove.data.targetX);

	if ($drag.moveY)
		$css.pixelTop($drag.target, onDragMove.data.targetY);

	$drag.sizeOverlays();

	return $evt.cancel(e);
}

Aeon.Drag.overlayFrames = function Drag$overlayFrames()
{
	var iframes = document.getElementsByTagName("iframe");
	if (iframes.length > 0)
	{
		if (!$drag.$overlays)
			$drag.$overlays = [];
		else
			$drag.removeOverlays();

		for (var i = 0; i < iframes.length; i++)
		{
			var iframe = iframes[i];
			if (iframe.offsetHeight > 0)
			{
				var overlay = document.createElement("div");
				overlay.iframe = iframe;
				overlay.style.cssText = $drag.overlayCss;

				document.body.appendChild(overlay);
				$drag.$overlays.push(overlay);
			}
		}

		$drag.sizeOverlays();
	}
};

Aeon.Drag.sizeOverlays = function Drag$sizeOverlays()
{
	if ($drag.$overlays)
	{
		for (var i = 0; i < $drag.$overlays.length; i++)
		{
			var overlay = $drag.$overlays[i];
			$css.pixelLeft(overlay, $dom.offsetLeft(overlay.iframe));
			$css.pixelTop(overlay, $dom.offsetTop(overlay.iframe));
			$css.pixelWidth(overlay, overlay.iframe.offsetWidth);
			$css.pixelHeight(overlay, overlay.iframe.offsetHeight);
		}
	}
};

Aeon.Drag.removeOverlays = function Drag$removeOverlays()
{
	if ($drag.$overlays == null)
		return;

	while ($drag.$overlays.length)
	{
		var oly = $drag.$overlays.shift();
		oly.parentNode.removeChild(oly);
	}
};


Aeon.RequestLoader = function RequestLoader()
{
	this.$super("onload", "oncomplete");

	this.$requests = [];
	this.$loadCount = 0;
}
Aeon.RequestLoader.inherits(Aeon.Dispatcher);

Aeon.RequestLoader.prototype.addRequest = function RequestLoader$addRequest(requestUrl)
{
	var request = new Aeon.WebRequest(requestUrl);
	request.addListener("oncomplete", Function.createDelegate(this, this.requestLoaded)); /**/
	this.$requests.push(request);

	return request;
}

Aeon.RequestLoader.prototype.requestLoaded = function RequestLoader$requestLoaded(event)
{
	var request = event.source;
	this.fireEvent("onload", { request: request });

	if (++this.$loadCount == this.$requests.length)
		this.fireEvent("oncomplete");
}

Aeon.RequestLoader.prototype.load = function RequestLoader$load()
{
	for (var i = 0; i < this.$requests.length; i++)
		this.$requests[i].execute();
}

Aeon.WebRequest = function WebRequest(url)
{
	this.$super("oncomplete", "onerror", "ontimeout");

	this.$url = url || String.EMPTY;
	this.$headers = { };
	this.$body = null;
	this.$httpVerb = null;
	this.$invokeCalled = false;
	this.$timeout = 0;
	this.$timer = null;
	this.$timedOut = false;
	this.$responseAvailable = false;

	this.responseText = null;
	this.responseXML = null;
	this.status = null;
	this.statusText = null;
}
Aeon.WebRequest.inherits(Aeon.Dispatcher);

Aeon.WebRequest.resolveUrl = function WebRequest$resolveUrl(url, baseUrl)
{
	if (url && url.indexOf('://') !== -1)
		return url;

	if (!baseUrl || baseUrl.length === 0)
	{
		var baseElement = $("base");
		if (baseElement && baseElement.href && baseElement.href.length > 0)
			baseUrl = baseElement.href;
		else
			baseUrl = document.URL;
	}
	var qsStart = baseUrl.indexOf('?');
	if (qsStart !== -1)
		baseUrl = baseUrl.substr(0, qsStart);

	qsStart = baseUrl.indexOf('#');
	if (qsStart !== -1)
		baseUrl = baseUrl.substr(0, qsStart);

	baseUrl = baseUrl.substr(0, baseUrl.lastIndexOf('/') + 1);
	if (!url || url.length === 0)
		return baseUrl;

	if (url.charAt(0) === '/')
	{
		var slashslash = baseUrl.indexOf('://');
		if (slashslash === -1)
			throw Error("Base URL does not contain ://.");

		var nextSlash = baseUrl.indexOf('/', slashslash + 3);
		if (nextSlash === -1)
			throw Error.argument("Base URL does not contain another /.");

		return baseUrl.substr(0, nextSlash) + url;
	}
	else
	{
		var lastSlash = baseUrl.lastIndexOf('/');
		if (lastSlash === -1)
			throw Error.argument("Cannot find last / in base URL.");

		return baseUrl.substr(0, lastSlash+1) + url;
	}
}

Aeon.WebRequest.createQueryString = function WebRequest$createQueryString(queryString, encodeMethod)
{
	if (!encodeMethod)
		encodeMethod = encodeURIComponent;

	var result = [];
	for (var arg in queryString)
	{
		if (Type.isFunction(queryString[arg]))
			continue;

		result.push(arg + "=" + Object.serialize(queryString[arg]));
	}
	return result.join("&");
}

Aeon.WebRequest.createUrl = function WebRequest$createUrl(url, queryString)
{
	if (!queryString)
		return url;

	var qs = Aeon.WebRequest.createQueryString(queryString);
	if (qs.length > 0)
	{
		var sep = "?";
		if (url && url.indexOf("?") !== -1)
			sep = "&";

		return url + sep + qs;
	}
	else
	{
		return url;
	}
}

Aeon.WebRequest.prototype.getUrl = function WebRequest$getUrl()
{
	return this.$url;
}

Aeon.WebRequest.prototype.setUrl = function WebRequest$setUrl(value)
{
	this.$url = value;
}

Aeon.WebRequest.prototype.getHeaders = function WebRequest$getHeaders()
{
	return this.$headers;
}

Aeon.WebRequest.prototype.getHttpVerb = function WebRequest$getHttpVerb()
{
	if (this.$httpVerb === null)
	{
		if (this.$body === null)
			return "GET";

		return "POST";
	}
	return this.$httpVerb;
}

Aeon.WebRequest.prototype.setHttpVerb = function WebRequest$setHttpVerb(value)
{
	this.$httpVerb = value;
}

Aeon.WebRequest.prototype.getBody = function WebRequest$getBody()
{
	return this.$body;
}

Aeon.WebRequest.prototype.setBody = function WebRequest$setBody(value)
{
	this.$body = value;
}

Aeon.WebRequest.prototype.getTimeout = function WebRequest$getTimeout()
{
	return this.$timeout;
}

Aeon.WebRequest.prototype.setTimeout = function WebRequest$setTimeout(value)
{
	this.$timeout = value;
}

Aeon.WebRequest.prototype.getResolvedUrl = function WebRequest$getResolvedUrl()
{
	return Aeon.WebRequest.resolveUrl(this.$url);
}

Aeon.WebRequest.prototype.execute = function WebRequest$execute()
{
	var body = this.getBody();
	var headers = this.getHeaders();
	var verb = this.getHttpVerb();
	var url = this.getResolvedUrl();

	this.$httpRequest = new XMLHttpRequest();
	this.$httpRequest.onreadystatechange = Function.createDelegate(this, this.onReadyStateChange); /**/
	this.$httpRequest.open(verb, url, true);

	if (headers)
		for (var header in headers)
			this.$httpRequest.setRequestHeader(header, headers[header]);

	if (verb.toLowerCase() === "post")
	{
		if ((headers === null) || !headers["Content-Type"])
			this.$httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');

		if (!body)
			body = String.EMPTY;
	}

	var timeout = this.getTimeout();
	if (timeout > 0)
		this.$timer = window.setTimeout(Function.createDelegate(this, this.onTimeout), timeout);

	try
	{
		this.$httpRequest.send(body);
	}
	catch(e)
	{
		var errorMessage = String.format("Failed to load {0}: {1}", this.$url, e.message);
		this.fireEvent("onerror", { message: errorMessage });
	}
}

Aeon.WebRequest.prototype.onReadyStateChange = function WebRequest$onReadyStateChange()
{
	if (this.$httpRequest.readyState === 4)
	{
		try
		{
			var errorMessage;
			if (typeof(this.$httpRequest.status) === "undefined")
			{
				errorMessage = String.format("Failed to load {0}: The http request status is undefined.", this.$url);
				$log.error(errorMessage);

				this.fireEvent(new Event("onerror", { message: errorMessage }));
				return;
			}
			else if (!(this.$httpRequest.status == 0 || this.$httpRequest.status == 200))
			{
				errorMessage = String.format("Failed to load {0}: {1}.", this.$url, this.$httpRequest.status);
				$log.error(errorMessage);

				this.fireEvent(new Event("onerror", { message: errorMessage }));
				return;
			}
		}
		catch(ex)
		{
			$log.error(ex.message);
			return;
		}

		this.clearTimer();
		this.$responseAvailable = true;

		this.responseText = this.$httpRequest.responseText;
		this.responseXML = this.$httpRequest.responseXML;
		this.status = this.$httpRequest.status;
		this.statusText = this.$httpRequest.statusText;

		this.fireEvent("oncomplete");

		this.$xmlHttpRequest = null;
	}
}

Aeon.WebRequest.prototype.onTimeout = function WebRequest$onTimeout()
{
	if (!this.$responseAvailable)
	{
		this.clearTimer();
		this.$timedOut = true;
		this.$xmlHttpRequest.onreadystatechange = new Function;
		this.$xmlHttpRequest.abort();

		this.fireEvent("ontimeout");

		this.$xmlHttpRequest = null;
	}
}

Aeon.WebRequest.prototype.clearTimer = function WebRequest$clearTimer()
{
	if (this.$timer != null)
	{
		window.clearTimeout(this.$timer);
		this.$timer = null;
	}
}

Aeon.Settings = function Settings()
{
}

Aeon.Settings.prototype.getValue = function Settings$getValue(propName, object, override, defaultValue)
{
	if (Type.isHtmlElement(override))
	{
		if (override.hasAttribute(propName))
			return override.getAttribute(propName);
	}
	else if (override && override[propName] != null)
		return override[propName];

	if (object && object[propName] != null)
		return object[propName];

	return defaultValue;
}

Aeon.Settings.prototype.getBoolean = function Settings$getBoolean(propName, object, override, defaultValue)
{
	var value = String(this.getValue(propName, object, override, defaultValue)).toLowerCase();
	if (value == "1" || value == "true" || value == "yes")
		return true;

	return false;
}

Aeon.Settings.prototype.getNumber = function Settings$getNumber(propName, object, override, defaultValue)
{
	var value = this.getValue(propName, object, override, defaultValue);
	if (isNaN(value))
		return 0;

	return parseInt(value);
}

Aeon.Settings.prototype.getString = function Settings$getString(propName, object, override, defaultValue)
{
	return String(this.getValue(propName, object, override, defaultValue));
}

Aeon.Settings.prototype.getFunction = function Settings$getFunction(propName, object, override, defaultValue)
{
	var result = this.getValue(propName, object, override, defaultValue);
	if (Type.isFunction(result))
		return result;

	return null;
}

/**
 * Contains various constant collections
 * @type {Object}
 */
Aeon.Constants = {};

/**
 * Contains commonly used key code constants
 * @type {Object}
 */
Aeon.Constants.Key =
{
	TAB: 9,
	CONTROL: 17,
	SHIFT: 16,
	ALT: 18,
	ENTER: 13,
	UP: 38,
	DOWN: 40,
	LEFT: 37,
	RIGHT: 39,
	DELETE: 46,
	BACKSPACE: 8
};

var $css = Aeon.Utils.Css;
var $dom = Aeon.Utils.Dom;
var $evt = Aeon.Utils.Evt;
var $xml = Aeon.Utils.Xml;
var $log = Aeon.Utils.Log;
var $cookie = Aeon.Utils.Cookie;
var $drag = Aeon.Drag;
var $const = Aeon.Constants;

var $assert = Aeon.Utils.Assert;

